A decorator is like a picture frame. It wraps the main object, adding some other functionality to it.
The main goal of the Decorator pattern is to enable the addition of functionalities or behaviors dynamically to objects.
To enable additional functionalities or behaviors in classes, a simple inheritance could easily do the job and no design pattern would be necessary. However, pay attention to the term “dynamically”. It means that the decorator must be instantiated after the original object already exists, and therefore inheritance couldn’t be the best solution.
In other words, a decorator usually receives the original object in its constructor and implements the same interfaces, adding some logic while calling the original object. The implementation of the same interfaces guarantees the transparency of the decorated object.
A great and good side effect of the pattern is that the additional functionalities provided by the decorator are of course implemented in other classes, avoiding adding complexity to the original class. This helps meet SOLID’s first principle, which is the Single Responsibility Principle, by guaranteeing the separation of responsibilities and makes the classes easily testable.
The Coffee Example
Suppose a Coffee interface and its concrete implementations SimpleCoffee and Espresso. The classes CoffeeWithMilk and SecretRecipeCoffee are decorators that will change behaviors by add ingredients and cost, or anything else.
The main difference in the implementation of the decorators and the other concrete classes is that the decorators wrap the original object while adding behavior.
And when executing the code below, it’s easy to find how the behavior changed.
These code examples were simplified.