Cover Image

Decorator

Lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors.

classDiagram
    class Component {
        <<interface>>
        +operation()
    }
    class ConcreteComponent {
        +operation()
    }
    class BaseDecorator {
        -Component wrappee
        +operation()
    }
    class ConcreteDecoratorA {
        +operation()
    }
    class ConcreteDecoratorB {
        +operation()
    }

    Component <|.. ConcreteComponent
    Component <|.. BaseDecorator
    BaseDecorator o-- Component
    BaseDecorator <|-- ConcreteDecoratorA
    BaseDecorator <|-- ConcreteDecoratorB

When to use

Use the Decorator pattern when you need to be able to assign extra behaviors to objects at runtime without breaking the code that uses these objects. Use when it’s awkward or not possible to extend an object’s behavior using inheritance.

Explanation

Problem

Imagine that you’re working on a notification library which lets other programs notify their users about important events. The initial version of the library was based on the Notifier class that had only a few fields, a constructor and a single send method. The method could accept a message argument and send it to a list of emails passed to the constructor.

Later, users wanted SMS notifications, then Facebook, then Slack. And they wanted to combine them. “Email + SMS”, “SMS + Slack”, “Email + SMS + Slack”. You can’t easy support all combinations with inheritance (Combinatorial Explosion).

Solution

Inheritance is static. Composition is dynamic. With composition, one object has a reference to another and delegates it some work. The Decorator pattern relies on composition. You have a Notifier interface. The basic Notifier sends emails. Decorators (SMSDecorator, SlackDecorator) implement Notifier, but they also contain a reference to another Notifier. They call the wrapped notifier’s send method, and then execute their own behavior.

Real world problem

  1. Clothing: You wear a Shirt. It’s cold, you put on a Sweater (decorate Shirt). Still cold, you put on a Jacket (decorate Sweater). If it rains, you put on a Raincoat. You can strip them off strictly in reverse order.
  2. Streams: Java I/O Streams. new BufferedReader(new FileReader(file)).
  3. Middlewares: Web frameworks use middleware to decorate request handling (Auth, Logging, Compression).

Pros and Cons

Pros Cons
- Extensibility: You can extend an object’s behavior without making a new subclass.
- Dynamic: You can add or remove responsibilities from an object at runtime.
- Combinations: You can combine several behaviors by wrapping an object into multiple decorators.
- Single Responsibility Principle: You can divide a monolithic class that implements many possible variants of behavior into several smaller classes.
- Removal: It’s hard to remove a specific wrapper from the wrappers stack.
- Order: It’s hard to implement a decorator in such a way that its behavior doesn’t depend on the order in the decorators stack.
- Complexity: The initial configuration code of layers might look ugly.

Comparison

  • Adapter: Adapter provides a different interface to the wrapped object. Proxy provides it with the same interface. Decorator provides it with an enhanced interface (dynamically).
  • Chain of Responsibility: Decorator allows you to handle a request, pass it to the next decorator (wrapper). Chain of Responsibility allows you to pass a request along a dynamic chain of potential handlers.

Code example

Typescript

Php