Decorator pattern falls under category of structural patterns. Purpose of Decorator pattern is to enable us to add additional features to an object dynamically at run time.
Why not subclassing?
One may argue to make use of subclassing an existing class to add additional features/functionalities to our class, and hence to its objects. This is in fact a well practiced approach, but it has some disadvantages apart from strong coupling between parent and child class.
First, parent class behaviour is added to all objects of the child class which may not be desired. Second, a behavior inherited from a parent class is locked into child class at compile time, and hence we can't influence the time at which we actually want object to have a particular behaviour.
Another major disadvantage can be that if there is scope for a large number of combinations of these behaviours in our system and we make classes for all possible combinations, then it may lead to class explosion, leading to huge maintenance problem.
Decorator pattern
Decorator pattern enables us to add additional features/responsibilities to the objects at run time in a very flexible manner. We can decide which behaviour to be added, when to be added, how to be added.
Decorator approach
Target object which we want to have a specific feature is wrapped inside another object having that feature. Enclosing object is called decorator, and importantly it has same interface as target object so that client code can invoke same interface. When client sends a request, decorator object forwards that request to target object and additionally add its own action(decoration) before or after it. Multiple decorators can be applied recursively, hence allowing to add as many features as we want.
We define the interface of the target object/class. And we define an abstract Decorator class which implements this interface, so now it has same interface as target object and client can invoke calls on it transparently.This abstract class also contains the reference to target object. We implement concrete classes for Decorator,wherein we direct client requests to target object.And while it does so,it also performs the 'decoration'.
Please keep in mind that all class based inheritance done here is just to make sure that a common interface is maintained across all objects involved in the process. Hence, we don't suffer from the disadvantages we discussed above in context of subclassing.
Code structure
Lets look into code structure we need to have to implement Decorator pattern.
UML diagram is as follows:
First, we have a class to define a common interface to be used by clients transparently:
We implement functionality in a class:
Our aim is to add additional features to objects of SimpleComponent at run time.
So we define an abstract decorator class:
Now we add specific decorators:
Lets now put it all together and see how it works:
Running this program would give following output:
Core functionality implemented hereDecoration done by DecoratorB
Decoration done by DecoratorA
So, we are able to add behaviours to target object dynamically. If needed, we can add additional features as and when we want, thus handing lots of flexibility to our system.
Example
java.io package can be looked into to see working example of Decorator pattern.
InputStream is an abstract class and is super class for all classes representing an input stream of bytes. It has an abstract method read() which every subclass needs to implement. This class can be mapped to Component class in UML diagram above.
There are several concrete implementations of InputStream, like FileInputStream which reads bytes from a file in the file system. FileInputStream can be mapped to SimpleComponent in our UML diagram.
There is another class, FilterInputStream. This class extends InputStream and contains some other input stream, which it uses as basic source of bytes, and possibly transforms the data or provides additional functionality. It can be mapped to Decorator class in UML diagram.
Finally, there are classes which extend FilterInputStream, like BufferedInputStream. This class adds functionality of buffering the input data.
So, if we need to read data from a file one character at a time:
What we may not like about Decorator?
A quite obvious downside of Decorator pattern is that sometimes it leads to too many small classes in the system which makes it easy to understand and debug.
No comments:
Post a Comment