Sunday, November 22, 2015

The Decorator design pattern

The Decorator is a structural design pattern and one of the so called "wrapper" patterns along with adapter, facade, proxy and bridge. The decorator allows new functionalities to be added to an object statically or dynamically without affecting the behavior of objects of the same class.  It is used often in cases where concrete implementations should be decoupled from responsibilities and behaviors or when subclassing to achieve modification is impractical/impossible. In simple terms, in the decorator pattern we wrap the object, add functionality to it and offer this new version for use.

To showcase an implementation of the decorator pattern in the tutorial, we will consider the construction of a house, which can be delivered as the simple (empty) building or with additional features such as being furnished, having jacuzzi and similar options. The UML class diagram for our tutorial is depicted in the following picture:

We start our tutorial with the 'House' interface which defines a single method, buildHouse()
package com.tasosmartidis.design_patterns_tutorial.decorator;

public interface House {

    void buildHouse();
}

The interface is implemented by classes which build either concrete
package com.tasosmartidis.design_patterns_tutorial.decorator;

public class ConcreteHouse implements House {

    public void buildHouse() {
        System.out.println("Building a concrete house...");     
    }
}
or wooden houses
package com.tasosmartidis.design_patterns_tutorial.decorator;

public class WoodenHouse implements House {

    public void buildHouse() {
        System.out.println("Building a wooden house...");       
    }
}

Next comes our decorator class which implements the House interface, but leaves the buildHouse() abstract so that different classes that extend the decorator class can implement the method adding new features (alternatively the method could be overridden). The code for the decorator class is shown below:
package com.tasosmartidis.design_patterns_tutorial.decorator;


public abstract class HouseDecorator implements House {

    protected House decoratedHouse;
    
    public HouseDecorator(House decoratedHouse) {
        this.decoratedHouse = decoratedHouse;
    }
    
    public abstract void buildHouse();
}

We will now define classes which add new features and decorate the basic construction of a house. We can delivered the house furnished
package com.tasosmartidis.design_patterns_tutorial.decorator;


public class FurnishedHouse extends HouseDecorator {

    public FurnishedHouse(House decoratedHouse) {
        super(decoratedHouse); 
    }
    
    public void buildHouse() {
        decoratedHouse.buildHouse();
        decorateWithFurniture();
    }
    
    private void decorateWithFurniture() {
        System.out.println("Adding furniture to the house...");
    }
}

as well as with jacuzzi for the people that want something more premium
package com.tasosmartidis.design_patterns_tutorial.decorator;


public class HouseWithJacuzzi extends HouseDecorator {

    public HouseWithJacuzzi(House decoratedHouse) {
        super(decoratedHouse); 
    }

    public void buildHouse() {
        decoratedHouse.buildHouse();
        decorateWithJacuzzi();
    }
    
    private void decorateWithJacuzzi() {
        System.out.println("Adding jacuzzi to the house...");
    }
}

So far, so good, now let's build some houses! We will create a demo application that builds a couple of houses using different decoration, as shown in the following code snippet:
package com.tasosmartidis.design_patterns_tutorial.decorator;

public class DecoratorDemo {

    public static void main(String[] args) {
        // Build a wooden house with furniture
        System.out.println("Wooden house with furniture:");
        House woodenHouse = new WoodenHouse();      
        House furnishedHouse = new FurnishedHouse(woodenHouse);     
        furnishedHouse.buildHouse();    
        
        System.out.println();
        
        // Build a concrete house with furniture and jacuzzi
        System.out.println("Concrete house with furniture & Jacuzzi:");
        House concreteHouse = new ConcreteHouse();
        House furnishedHouseWithJacuzzi = new HouseWithJacuzzi(new FurnishedHouse(concreteHouse));      
        furnishedHouseWithJacuzzi.buildHouse();

    }
}

Running the application yields the following output:












That's all folks! The code is as always available in GitHub.

No comments:

Post a Comment