⚠️ The following post confuses the Decorator Pattern with the Proxy Pattern. While the structures of those Patterns are the same, they aim for different goals. The Decorator adds functionality to the underlying object, whereas a Proxy controls access to it. Therefor the usage of a cache is rather a Proxy than a Decorator.
The Decorator PatternClass Diagram:
Decorator Pattern is one of the most powerful Design Patterns, and its important to understand why. It allows you to add functionality to a Class without changing it. You use delegation instead of inheritance by just wrapping the existing Class in a "Suite" of your choice while the Interface stays the same. This way, you can even wrap a Class into multiple layers of Decorators where every Layer has its own and different purpose, and the Original Class even stays the same. Other than that the Layers are completely interchangeable and removable, because they all have the same Interface.
You can use your naked Object in the same manner as a decorated one with multiple Layers, and the rest of the Application wont even notice. Other components dont need to know whether a Class is decorated or not, because the only thing they care about is the Interface. The following Graphic should clear things up:
In the first example there is no Decorator. So SomeClass directly speaks to BookService
In the second example there is a single Decorator. Therefore the following steps apply
- SomeClass calls SomeDecoratorA.
- SomeDecoratorA delegates the call to the real BookService. The Decorator could even manipulate the call in this step.
- BookService returns a result. SomeDecoratorA now holds the result, and may manipulate it.
- Finally, SomeDecoratorA hands the result over to the original caller SomeClass.
Usage ExamplesMost examples in Books and online Guides describe the Decorator Pattern used by some Graphic related stuff. You would have a normal line, and a line decorated with a shadow. Another example would be a Window in a GUI where you vary Decorators that add different kinds of styles (e.g. different Colors).
But its important to understand, that Decorators are not intended to just make Styling more dynamic. It is just one way to use them. Instead, there are many more great use cases for Decorators and i will show you an example applying cache functionality.
Applying Cache with Decorator PatternClass Diagram:
As you can see i am adding a BookServiceCacheDecorator that holds a Cache Component which temporarily stores a List of Books. The BookServiceCacheDecorator in the first place asks the Cache if there is a result available. If thats true, the decorator will use the cached result. Otherwise it will fetch the Result of the BookService itself, and store the Result in the Cache for later use.
I start with the BookService Interface,
and its prime implementation.
Here is the Code of the Cache Class. In this example it's caching forever, but thats alright.
Now lets continue with the Decorator Base Class. It has a preemtive Constructor to hand over the decorated Object.
And finally the CacheDecorator. It Checks the Cache before calling BookServiceImpl.
Now two different use cases. One without Decorator, and one with the Cache Decorator:
Thats basically it. Just read the Code carefully. The final use cases should make clear how the Decorator applies functionality. Decorator pattern really helps you create a very modular Code in many circumstances. You should not be afraid of the additional Classes the Pattern depends on, since they are really not a big deal. The modularity gained easily outweighs them.