"Decorator Pattern", the best way to add behaviors to objects at runtime.
In this article we start talking about structural patterns, and let's begin with what is perhaps the most complicated of structural patterns: the Decorator.
In the previous articles we analyzed the most important creational patterns, that is, those patterns that provide various object creation mechanisms, which increase flexibility and reuse of existing code. With the Decorator instead we begin to analyze the structural patterns. Structural design patterns explain how to assemble objects and classes into larger structures, while keeping these structures flexible and efficient.
The Decorator pattern (1.) fully belongs to this important category of structural patterns and it is used to add additional features or behaviors to a particular instance of a class, while not modifying the other instances of same class. Decorators provide a flexible alternative to sub-classing for extending functionality. Is very important to understand Decorator because once we know the techniques of decorating, we’ll be able to give our objects new responsibilities without making any code changes of the class. Interesting, isn’t it?
As usual before analyzing the pattern we start to look for a Real-World Analogy. (see the picture below).
Figure 1: We get a combined effect by wearing multiple pieces of clothing.
Wearing clothes is an example of using decorators. When we are cold, we wrap ourselves in a sweater. If we are still cold with a sweater, we can wear a jacket on top. If it’s raining, we can put on a raincoat. All of these clothes “extend” our basic behavior but aren’t part of us, and we can easily take off any piece of clothing whenever we don’t need it (see the image on figure 1).
Let's go back to the world of software and talk about the genesis of the pattern decorator.
When object-oriented programming was introduced, inheritance was the primary model used to extend object functionality. However this is not always the right approach. In fact, it has been shown that extending objects using inheritance often results in a phenomenon known as Exploding class hierarchy. In addition, inheritance also has another problem, it is static. We cannot change the behavior of an existing object at runtime. We can only replace the entire object with another created from a different subclass. Then there is also an additional limitation, in most languages, inheritance does not allow a class to inherit the behaviors of multiple classes at the same time, i.e. subclasses can only have one parent class.
One of the ways to overcome these warnings is to use Composition instead of Inheritance. Both alternatives work almost the same: with composition one object has a reference to another and delegates the work to it, while with inheritance, the object itself is able to do that work, inheriting the behavior from its superclass.
But with composition it is possible to overcome the limits of inheritance seen above.
The Decorator design pattern provides a flexible alternative to inheritance in order to extend the functionality of objects. This pattern allows us to dynamically enrich, at run-time, an object with new features: it is possible to stack one or more decorators on top of each other, each adding new features.
Here are the advantages of the decorator over inheritance:
• A Decorator acts at runtime unlike inheritance which extends the behavior of the parent class at compile time with subclasses.
• A Decorator can operate on any implementation of a given interface, eliminating the need to create subclasses of an entire class hierarchy.
• The subclass adds compile time behavior and the change affects all instances of the original class; the decorator pattern can provide new runtime behaviors for individual objects.
• The use of the decorator model leads to a clean and testable code. Services created with inheritance cannot be tested separately from its parent class because there is no mechanism to replace a parent class with a stub.
Obviously, the main disadvantage is the complexity of the implementation of this pattern compared to the simple implementation of inheritance.
Now let's see the structure of this pattern, in figure 2 we see the class diagram of decorator pattern.
Figure 2: decorator pattern structure.
we have the following classes:
1. The Component defines the interface for objects to which responsibilities can be added dynamically.
2. Concrete Component, is the original object to which the additional responsibilities are added in program.
3. Decorator, this is an abstract class which contains a reference to the component object and also implements the component interface. The Decorator delegates all operations to the wrapped object.
4. Concrete Decorators define extra behaviors that can be added to components dynamically. Concrete decorators override methods of the base decorator and execute their behavior either before or after calling the parent method.
5. The Client can wrap components in multiple layers of decorators, as long as it works with all objects via the component interface.
It works like this:
We have an instance, and we put another instance inside of it. They both support the same (or similar) interfaces. The one on the outside is a “decorator.” We use the one on the outside. It either masks, changes, or pass-troughs the methods of the instance inside of it.
Now let's see a first concrete application of the pattern decorator, let's consider the following problem: suppose we are working on a user interface toolkit and we wish to support adding borders and scroll bars to windows. We could define an inheritance hierarchy like this one below.
Figure 3: window hierarchy inheritance for GUI toolkit.
As we can see this solution easily produces a very deep inheritance hierarchy.
But the Decorator pattern suggests giving the client the ability to specify whatever combination of "features" is desired.
This flexibility can be achieved with the following design.
Figure 4: decorator pattern structure for GUI toolkit.
Now let's see the Kotlin language implementation of the solution with the decorator pattern for our graphic toolkit.
These are the implementation steps
1. create the "Drawable" (Component) interface where we can declare the methods whose behavior we want to dynamically enrich (draw method)
2. create the "Window" class (Base Component) that implements the Drawable interface and define the basic behavior in it.
3. Create a base decorator class, “WindowDecorator”. It should have a field to store a reference to a wrapped object. The field must be declared "Drawable" to allow linking to concrete components and decorators. The base decorator must delegate all work to the wrapped object.
4. Make sure all classes implement the Drawable interface.
5. Create concrete decorators by extending them from the base decorator (“Border”, “HorizontalScrollBar”, “VerticalScrollBar”). A concrete decorator must execute its behavior before or after the call to the parent method (which always delegates to the wrapped object).
6. The client code (“DecoratorTest”) must be responsible for creating decorators and composing them in the way the client needs.
Figure 5: kotlin source code implementation for GUI toolkit.
Now let's see another interesting problem that we can solve thanks to Decorator pattern.
Let's imagine we are working on a notification library that allows other programs to notify their users of important events.
The initial version of the library is based on the Notifier class which has only a few fields, a constructor, and a single submission method.
The method could take a message from a client and send the message to a list of emails that have been passed to the notifier via its constructor.
A third-party app acting as a client has to create and configure the notifier object once, then use it whenever something important happened.
At some point they ask us to extend our library which beyond to sending e-mail notifications must be able to send a SMS for particular critical issues or even to be able to notify on Facebook rather than Teams for corporate users. Furthermore, the library must allow you to use multiple types of notification at the same time.
The most natural approach is to extend the Notifier class and insert the additional notification methods into new subclasses.
The client can now instantiate the desired notification class and use it for all further notifications.
Then to be able to use multiple notification types at the same time we have to create special subclasses that combine different notification methods within a class. However, this approach eventually will bloat the code. We need to find another way to structure the notification classes so that their number does not lead to an “Exploding Class Hierarchy” (see Figure below).
Figure 6: Notifier hierarchy inheritance for Notification Library.
One of the ways to overcome this problem is to use composition instead of inheritance.
Composition is the key principle behind many design patterns, including Decorator.
In figure 7 we can see the implementation of Decorator Pattern to our Notification Library.
Figure 7: decorator pattern structure for Notification Library.
And now we are going to implement this solution in C++ language.
Figure 8: C++ source code implementation for Notification Library.
Here it is the output.
as we can see the code works well.
Now let's make a last example that we will implement in Java language.
This is a more didactic example: we have to print a Menu. At first, we simply want to add product names to a Menu and display it.
But then we apply a first extension: we should add an index before the name.
Later on we have to apply a second extension too: frame the names to have a nice menu.
A snippet code below shows a first solution. It is a trivial solution obtained with inheritance.
Figure 9: Java source code implementation for hierarchy inheritance Menu.
Here it is the output.
This solution is not very good regarding the extendibility aspect. What we have to do if we want to mix behaviors? For instance, we want a FramedIndexedMenu? Or if we want any other kind of menu, e.g., a RightToLeftMenu.
We should want to be able to combine its behavior with that of one or more other classes ( IndexedRightToLeftMenu, FramedRightToLeftMenu, FramedIndexedRightToLeftMenu).
What we do in order to solve this problem?
In the previous examples we saw that using inheritance in contexts like these leads to an explosion in the number of classes.
But we also learned how to use composition to solve these problems using the pattern decorator.
The decorator pattern allows us to solve problems like this, where we need dynamically extensibility.
The snippet code below is the solution to Product Menu problem with decorator, as you can see now it is very simple create a FramedIndexedMenu.
Figure 10: Java source code implementation of decorator pattern for Menu problem.
Here it is the output.
as we can see the code works well.
We conclude this article by mentioning some known commercial uses of the pattern decorator.
Many object-oriented GUI toolkits use decorators to add graphical embellishments to widgets. An example is QT cross framework library.
Another known use of the Decorator pattern is the "streams" classes in Java IO library classes (eg "BufferedInputStream"). Let's detail in more depth the topic of streams (I refer to an interesting example proposed by the GoF (1.)).
Streams may be considered as fundamental abstraction in most I/O library. A stream can provide an interface for converting objects into a sequence of bytes or characters. This lets us transcribe an object to a file or to a string in memory for retrieval later. A straightforward way to do this is to define an abstract Stream class with subclasses MemoryStream and FileStream. But suppose we also want to be able to do the following:
1) Compress the stream data using different compression algorithms.
2) Reduce the stream data to 7-bit ASCII characters so that it can be transmitted over an ASCII communication channel.
The Decorator pattern gives us an elegant way to add these responsibilities to streams. The diagram below shows the implementation of Decorator for Stream Class Library:
Figure 11: implementation of decorator pattern for Stream I/O class library.
The Stream abstract class maintains an internal buffer and provides operations for storing data onto the stream (PutByte, PutString). Whenever the buffer is full, Stream calls the abstract operation HandleBufferFull, which does the actual data transfer. The FileStream version of this operation overrides this operation to transfer the buffer to a file.
The key class is StreamDecorator, which maintains a reference to a component stream and forwards requests to it. StreamDecorator subclasses override HandleBufferFull and perform additional actions before calling HandleBufferFull of StreamDecorator.
For example, the CompressingStream subclass compresses the data, and the ASCII7Stream converts the data into 7-bit ASCII. Then, to create a FileStream that compresses its data and converts the compressed binary data to 7-bit ASCII, we decorate a FileStream with a CompressingStream and an ASCII7Stream (see the code snippet below).
That's it for the "Decorator Pattern".
However, I want to conclude this article with a final consideration.
There is also another approach tackle the problem of inheritance limits in programming languages. It is a solution that completes the world of object oriented design by enriching it with the concept of ASPECT. Aspect-oriented programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns (there are AOP extensions for many languages, for example Java, C# and C++). But this is a topic that deserves its own dedicated series. We will see it, I think in the first months of 2023.
I remind you my newsletter "Sw Design & Clean Architecture": https://lnkd.in/eUzYBuEX where you can find my previous articles and where you can register, if you have not already done, so to be notified every time I publish new articles.
thanks for reading my article, and I hope you have found the topic useful,
Feel free to leave any feedback.
Your feedback is very appreciated.
Thanks again.
Stefano
References:
1. Gamma, Helm, Johnson, Vlissides, “Design Patterns”, Addison Wesley (2° Edition October 2002). pp 175-184.
2. Alexander Shvets, “Drive Into Design Patterns”, Refactoring.Guru, 2021.