© Vaskaran Sarcar 2019
Vaskaran SarcarJava Design Patternshttps://doi.org/10.1007/978-1-4842-4078-6_7

7. Decorator Pattern

Vaskaran Sarcar1 
(1)
Bangalore, Karnataka, India
 

This chapter covers the decorator pattern.

GoF Definition

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

Concept

This pattern says that the class must be closed for modification but open for extension; that is, a new functionality can be added without disturbing existing functionalities. The concept is very useful when we want to add special functionalities to a specific object instead of the whole class. In this pattern, we try to use the concept of object composition instead of inheritance. So, when we master this technique, we can add new responsibilities to an object without affecting the underlying classes.

Real-World Example

Suppose you already own a house. Now you have decided to build an additional floor on top of it. You may not want to change the architecture of the ground floor (or existing floors), but you may want to change the design of the architecture for the newly added floor without affecting the existing architecture.

Figure 7-1, Figure 7-2, and Figure 7-3 illustrate this concept.
../images/395506_2_En_7_Chapter/395506_2_En_7_Fig1_HTML.jpg
Figure 7-1

Original house

../images/395506_2_En_7_Chapter/395506_2_En_7_Fig2_HTML.jpg
Figure 7-2

Original house with a decorator (new structure is built on top of original structure)

../images/395506_2_En_7_Chapter/395506_2_En_7_Fig3_HTML.jpg
Figure 7-3

Creating an additional decorator from an existing one (and painting the house)

Note

Case 3 is optional. You can use already a decorated object to enhance the behavior in this way or you can create a new decorator object and put all the new behavior in it.

Computer-World Example

Suppose that in a GUI-based toolkit, we want to add some border properties. We can do this with inheritance. But it cannot be treated as an ultimate solution because the user cannot have absolute control over this creation from the beginning. So, the core choice is static in this case.

Decorators comes into picture with a flexible approach. They promote the concept of dynamic choices, for example, we can surround the component in another object. The enclosing object is called a decorator. It must conform to the interface of the component that it decorates. It forwards the requests to the component. It can perform additional operations before or after the forwardings. An unlimited number of responsibilities can be added with this concept.

Note

You can notice the use of the decorator pattern in the I/O streams implementations in both .NET Framework and Java. For example, the java.io.BufferedOutputStream class can decorate any java.io.OutputStream object.

Illustration

Go through the following example. Here we never tried to modify the core makeHouse() method . We have created two additional decorators: ConcreteDecoratorEx1 and ConcreteDecoratorEx2 to serve our needs but we kept the original structure intact.

Class Diagram

Figure 7-4 shows the class diagram for the illustration of the decorator pattern.
../images/395506_2_En_7_Chapter/395506_2_En_7_Fig4_HTML.jpg
Figure 7-4

Class diagram

Package Explorer View

Figure 7-5 shows the high-level structure of the program.
../images/395506_2_En_7_Chapter/395506_2_En_7_Fig5_HTML.jpg
Figure 7-5

Package Explorer view

Implementation

Here’s the implementation.
package jdp2e.decorator.demo;
abstract class Component
{
    public abstract void makeHouse();
}
class ConcreteComponent extends Component
{
    public void makeHouse()
    {
        System.out.println("Original House is complete. It is closed for modification.");
    }
}
abstract class AbstractDecorator extends Component
{
    protected Component component ;
    public void setTheComponent(Component c)
    {
        component = c;
    }
    public void makeHouse()
    {
        if (component != null)
        {
            component.makeHouse();//Delegating the task
        }
    }
}
//A floor decorator
class FloorDecorator extends AbstractDecorator
{
    public  void makeHouse()
    {
        super.makeHouse();
        //Decorating now.
        System.out.println("***Floor decorator is in action***");
        addFloor();
        /*You can put additional stuffs as per your need*/
    }
    private void addFloor()
    {
        System.out.println("I am making an additional floor on top of it.");
    }
}
//A paint decorator
class PaintDecorator extends AbstractDecorator
{
    public void makeHouse()
    {
        super.makeHouse();
        //Decorating now.
        System.out.println("***Paint decorator is in action now***");
        paintTheHouse();
        //You can add additional stuffs as per your need
    }
    private void paintTheHouse()
    {
        System.out.println("Now I am painting the house.");
    }
}
public class DecoratorPatternExample {
    public static void main(String[] args) {
        System.out.println("***Decorator pattern Demo*** ");
        ConcreteComponent withoutDecorator = new ConcreteComponent();
        withoutDecorator.makeHouse();
        System.out.println("_________________");
        //Using a decorator to add floor
        System.out.println("Using a Floor decorator now.");
        FloorDecorator floorDecorator = new FloorDecorator();
        floorDecorator.setTheComponent(withoutDecorator);
        floorDecorator.makeHouse();
        System.out.println("_________________");
        //Using a decorator to add floor to original house and then
        //paint it.
        System.out.println("Using a Paint decorator now.");
        PaintDecorator paintDecorator = new PaintDecorator();
        //Adding results from floor decorator
        paintDecorator.setTheComponent(floorDecorator);
        paintDecorator.makeHouse();
        System.out.println("_________________");
    }
}

Output

Here’s the output.
***Decorator pattern Demo***
Original House is complete. It is closed for modification.
_________________
Using a Floor decorator now.
Original House is complete. It is closed for modification.
***Floor decorator is in action***
I am making an additional floor on top of it.
_________________
Using a Paint decorator now.
Original House is complete. It is closed for modification.
***Floor decorator is in action***
I am making an additional floor on top of it.
***Paint decorator is in action now***
Now I am painting the house.
_________________

Q&A Session

  1. 1.

    Can you explain how composition is promoting a dynamic behavior that inheritance cannot?

    We know that when a derived class inherits from a parent class, it inherits the behavior of the base class at that time only. Though different subclasses can extend the base/parent class in different ways, this type of binding is known in compile-time, so the choice is static in nature. But the way that you used the concept of composition in the example lets you experiment with dynamic behavior.

    When we design a parent class, we may not have enough visibility about what kind of additional responsibilities our clients may want in later phases. And our constraint is that we should not modify the existing code frequently. In such a case, object composition not only outclasses inheritances, it also ensures that we are not introducing bugs to the existing architecture.

    Lastly, in this context, you must remember one of the key design principles: Classes should be open for extension but closed for modification.

     
  2. 2.
    What are the key advantages of using a decorator?
    • The existing structure is untouched, so that you are not introducing bugs there.

    • New functionalities can be easily added to an existing object.

    • You do not need to predict/implement all the supported functionalities at the initial design phase. You can develop incrementally (e.g., add decorator objects one by one to support incremental needs). You must acknowledge the fact that if you make a complex class first, and then you try to extend the functionalities, it will be a tedious process.

     
  3. 3.

    How is the overall design pattern different from inheritance?

    You can add or remove responsibilities by simply attaching or detaching decorators. But with a simple inheritance mechanism, you need to create a new class for the new responsibilities. So, it is possible that you may end up with a complex system.

    Consider the example again. Suppose that you want to add a new floor, paint the house, and do some extra work. To fulfill this need, you start with decorator2 because it is already providing the support to add a floor to the existing architecture, and then you can paint it. So, you can add a simple wrapper to complete those additional responsibilities.

    But if you start with inheritance from the beginning, then you may have multiple subclasses (e.g., one for adding a floor, one for painting the house). Figure 7-6 shows hierarchical inheritance.

     
../images/395506_2_En_7_Chapter/395506_2_En_7_Fig6_HTML.jpg
Figure 7-6

A hierarchical inheritance

If you need an additional painted floor with extra features, you may end up with a design like the one shown in Figure 7-7.
../images/395506_2_En_7_Chapter/395506_2_En_7_Fig7_HTML.jpg
Figure 7-7

A class (Extra Features) needs to inherit from multiple base classes

Now you feel the heat of the diamond effect because in many programming languages including Java, multiple parent classes are not allowed.

In this context, even if you consider multilevel inheritance, you discover that overall the inheritance mechanism is much more challenging and time-consuming than the decorator pattern, and it may promote duplicate code in your application. Lastly, you must remember that inheritance mechanism is promoting only compile-time binding (not the dynamic binding).
  1. 4.

    Why can’t multilevel inheritance score higher in the previous context?

    Let’s assume that the Paint class is derived from Additional Floor, which in turn is derived from the Core Architecture. Now if your client wants to paint the house without creating an additional floor, the decorator pattern surely outclasses the inheritance mechanism because you can simply add a decorator to the existing system that supports the paint only.

     
  2. 5.

    Why are you creating a class with a single responsibility? You could make a subclass that can simply add a floor and then paint. In that case, you end up with fewer subclasses. Is this understanding correct?

    If you are familiar with SOLID principles, you know that there is a principle called single responsibility . The idea behind this principle is that each class should have a responsibility over a single part of the functionality in the software. The decorator pattern is very much effective when you use the single responsibility principle because you can simply add/remove responsibilities dynamically.

     
  3. 6.

    What are the disadvantages associated with this pattern?

    I believe that if you are careful enough, there is no significant disadvantage. But you must be aware of the fact that if you create too many decorators in the system, it will be hard to maintain and debug. So, in that case, it can create unnecessary confusion.

     
  4. 7.

    In the example, there is no abstract method in the AbstractDecorator class. How is this possible?

    In Java, you can have an abstract class without any abstract method in it, but the reverse is not true; that is, if a class contains at least one abstract method, then the class itself is incomplete and you are forced to mark it with the abstract keyword.

    Let’s revisit the AbstractDecorator class in the comment shown in bold.
    abstract class AbstractDecorator extends Component
    {
        protected Component component ;
        public void setTheComponent(Component c)
        {
            component = c;
        }
        public void makeHouse()
        {
          if (component != null)
            {
             component.makeHouse();//Delegating the task
            }
        }
    }

    You can see that I am delegating the task to a concrete decorator because I want to use and instantiate the concrete decorators only.

    Also, in this example, you cannot simply instantiate an AbstractDecorator instance because it is marked with the abstract keyword.

    The following line creates the Cannot instantiate the type AbstractDecorator compilation error.
    AbstractDecorator abstractDecorator = new AbstractDecorator();
     
../images/395506_2_En_7_Chapter/395506_2_En_7_Figa_HTML.jpg
  1. 8.
    In your example, instead of using concrete decorators, you could use the concept of polymorphism in the following way to generate the same output.
    System.out.println("Using a Floor decorator now."); //FloorDecorator floorDecorator = new FloorDecorator();
    AbstractDecorator floorDecorator = new FloorDecorator();
    floorDecorator.setTheComponent(withoutDecorator);
    floorDecorator.makeHouse();
    //Using a decorator to add floor to original house and then paint //it.
    System.out.println("Using a Paint decorator now.");
    //PaintDecorator paintDecorator = new PaintDecorator();
    AbstractDecorator paintDecorator = new PaintDecorator();
    //Adding results from decorator1
    paintDecorator.setTheComponent(floorDecorator);
    paintDecorator.makeHouse();
    System.out.println("_________________");

    Is this correct?

    Yes.

     
  2. 9.

    Is it mandatory to use decorators for dynamic binding only?

    No. You can use both static and dynamic binding. But dynamic binding is its strength, so I concentrated on it. You may notice that the GoF definition also focused on dynamic binding only.

     
  3. 10.

    You are using decorators to wrap your core architecture. Is this correct?

    Yes. The decorators are wrapper code to extend the core functionalities of the application. But the core architecture is untouched when you use them.

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

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