Implementing the decorator pattern

The decorator design pattern is a structural, hierarchical pattern that is used to dynamically extend or alter the functionality of objects at runtime. This pattern builds the functionality at each level without altering the existing structure using the composition from similar data types. It does this by wrapping them in an object of a decorator class, providing the additional functionality and leaving the original object intact without any modification.

Using this pattern, we can add functionality by wrapping an object inside another without affecting the other parts of the hierarchy in the system. This is also known as a wrapper as it wraps the objects. This differs from adding functionalities to objects at design-time, which generally involves inheritance. To add any functionality, we have to create a new class and create a subclass that extends a base class. With inheritance, we can add functionalities to classes by extending a base class. A problem with this approach is that a new class is created for every possible combination of these types, resulting in a class explosion. This means that there will be many classes available just to have these functionalities. This can be achieved without actually creating a higher number of classes with the decorator pattern.

The decorator pattern attaches the additional responsibilities to an object dynamically without making any modifications to existing classes. This provides a flexible alternative to subclassing for adding the additional functionality.

The decorator pattern makes the design flexible. Applications have to be designed to be flexible enough to accommodate any functionalities for changing requirements. We also want to keep in mind that the base class should not be modified. This pattern is aligned with the open-closed design principle, which states that the classes should be open for extension, but closed to any modifications.

The decorator design pattern can be used when we don't want too many subclasses that would extend from a base class for every combination. This is more suitable when we want to add new functionality to a legacy system in cases in which we don't want to modify existing classes but still need a new responsibility to be handled. It makes more sense to use the decorator pattern to add the additional responsibilities than providing subclasses to add the functionality.

The key idea behind the decorator pattern is to wrap the object being decorated. Consequently, it is also known as a wrapper pattern.

Let's illustrate the decorator pattern via an example. Consider the example of purchasing windows. In this case, the windows can be different sizes, colors, and materials. If we follow inheritance, just imagine the number of classes that we have to create for all these sets of different combinations. Instead of using inheritance, we can use decorators for these combinations when choosing a window. For example, we may want to create a small, silver window made of glass. This is shown in the following diagram:

We wrap the SmallWindow object in a GlassDecorator object, which again gets wrapped in a BrightSilver color decorator object. Any combination of this sort can be created. We started with a window object and added the material type and color. We then wrapped one object in another. The price of the window will be calculated on the outermost decorator object, which is the sum of the window, material type, and the color. Note that these decorations can be wrapped dynamically at runtime:

With these types, we can create any combination of windows, using the window shape, material type, and color. Let's see this in action. 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset