{
    "slug": "decorator_pattern",
    "term": "Decorator Pattern",
    "category": "general",
    "difficulty": "intermediate",
    "short": "Wraps an object to add new behaviour dynamically without modifying its class or using inheritance.",
    "long": "The Decorator pattern wraps an existing object in a new class that implements the same interface, adding behaviour before/after delegating to the wrapped object. Unlike inheritance, decorators compose at runtime and can be stacked. Classic examples: adding logging, caching, or rate limiting to a repository by wrapping it with LoggingRepository, CachingRepository, etc. — each wrapper transparent to callers. In PHP, this is the basis for middleware pipelines in frameworks and PSR-15 HTTP middleware.",
    "aliases": [
        "decorator",
        "wrapper decorator",
        "decorating objects"
    ],
    "tags": [
        "general",
        "design-pattern",
        "oop",
        "structural"
    ],
    "misconception": "Decorators always need to implement the same interface as the decorated class. While interface parity is the cleanest approach, transparent decorators require it — opaque decorators that add new behaviour without mimicking the original interface are valid for specific use cases.",
    "why_it_matters": "The Decorator pattern wraps an object to add behaviour dynamically — stacking decorators composes functionality without subclassing or modifying the original class.",
    "common_mistakes": [
        "Not implementing the same interface as the decorated object — breaks transparent substitution.",
        "Decorators with too much logic — each decorator should add one distinct concern.",
        "Confusing Decorator (wraps an object) with Proxy (controls access) — similar structure, different intent.",
        "Not delegating to the wrapped object — forgetting to call parent methods breaks the chain."
    ],
    "when_to_use": [
        "Adding cross-cutting concerns (logging, caching, timing, auth checks) to an existing class without modifying it.",
        "Building middleware pipelines where each layer wraps the next.",
        "Composing behaviour at runtime from interchangeable building blocks.",
        "Open-closed principle — extending a finalised or third-party class you cannot subclass."
    ],
    "avoid_when": [
        "You need to decorate dozens of methods — every method must be proxied, creating massive boilerplate.",
        "The wrapped object's interface changes frequently — every signature change must be updated in all decorators.",
        "A simple subclass or trait would achieve the same result with less complexity.",
        "The decoration logic is not reusable across multiple contexts — a one-off wrapper is not worth the abstraction."
    ],
    "related": [
        "open_closed_principle",
        "caching",
        "observer_pattern"
    ],
    "prerequisites": [
        "interfaces",
        "composition_over_inherit",
        "single_responsibility"
    ],
    "refs": [
        "https://refactoring.guru/design-patterns/decorator"
    ],
    "bad_code": "// Behaviour added via inheritance — class explosion:\nclass Logger extends UserRepository {}\nclass CachingLogger extends Logger {}\nclass MetricsLogger extends CachingLogger {}\n// Every combination needs a new subclass\n\n// Decorator — composable:\n$repo = new MetricsDecorator(new CachingDecorator(new UserRepository()));",
    "good_code": "interface Cache {\n    public function get(string $key): mixed;\n    public function set(string $key, mixed $value, int $ttl = 3600): void;\n}\n\nclass LoggingCache implements Cache {\n    public function __construct(\n        private Cache           $inner,\n        private LoggerInterface $logger,\n    ) {}\n\n    public function get(string $key): mixed {\n        $value = $this->inner->get($key);\n        $this->logger->debug('cache ' . ($value !== null ? 'hit' : 'miss') . \" for $key\");\n        return $value;\n    }\n    public function set(string $key, mixed $value, int $ttl = 3600): void {\n        $this->inner->set($key, $value, $ttl);\n    }\n}\n\n$cache = new LoggingCache(new RedisCache($redis), $logger);",
    "quick_fix": "Add behaviour to an object by wrapping it in a decorator that implements the same interface — stack multiple decorators to combine behaviours without subclassing",
    "severity": "low",
    "effort": "medium",
    "created": "2026-03-15",
    "updated": "2026-03-25",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/decorator_pattern",
        "html_url": "https://codeclaritylab.com/glossary/decorator_pattern",
        "json_url": "https://codeclaritylab.com/glossary/decorator_pattern.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[Decorator Pattern](https://codeclaritylab.com/glossary/decorator_pattern) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/decorator_pattern"
            }
        }
    }
}