Decorator

If you read the third chapter of Head First Design Patterns you would understand where the stuff I writing here comes. Actually, I have written this post when I was reading that book, as a way to have even more fun.

The Decorator pattern is a way to attach extra responsibilities to an object dynamically. In this sense it could be seen as an alternative to subclassing for extending functionality.

The idea of this pattern is that we have Component, an abstract class that a ConcreteComponent should extends. We create a lateral hierarchy of decorators that are based on the Decorator abstract class that extends Component. Each decorator HAS-A Component, that is, the object that it improves. Any decorator IS-A Component, since it extends the Component through Decorator, but this is used only for type matching, to give the decorator the flexibility of being used as a Component, but we are not using the public inheritance to get the Component behavior.

As an example we can think to a way to manage a hierarchy of beverages, were each base beverage could be enhanced by a series of condiments.

The base class of our hierarchy is Beverage, and any concrete beverege would extends it, as an Espresso. The Condiment abstract class would extends Beverage and would be the base for any components that we could add to our beverages. We'll have, for instance a Milk condiment. So, to get a espresso with milk we'll create a Milk object passing it a Espresso object as parameter.

Let's see the code for that. Here is the base of the beverages hierarchy:
public abstract class Beverage {
   String description;

   public String getDescription() {
      return description;
   }

   public abstract double cost();
}
The class is abstract, it can't be instantiate directly. We need a concrete beverage to extend it, defining the description and the cost for the beverage. This is done here, for the espresso:
public class Espresso extends Beverage {
   public Espresso() {
      description = "Espresso";
   }

   public double cost() {
      return 1.99;
   }
}
The condiments are based on Decorator, that derives from Beverage itself:
public abstract class Decorator extends Beverage {
   protected Beverage beverage;

   public Decorator(Beverage beverage) {
      this.beverage = beverage;
   }

   @Override
   public abstract String getDescription();
}

The decorator has a reference to the beverage it modifies, that is set by the constructor. The getDescription() method overrides the Beverage one, and has to be overridden by the concrete decorators. Let's see a couple of condiments:
public class Mocha extends Decorator {
   public Mocha(Beverage beverage) {
      super(beverage);
   }

   @Override
   public String getDescription() {
      return beverage.getDescription() + ", Mocha";
   }

   @Override
   public double cost() {
      return beverage.cost() + 0.20;
   }
}

// ...

public class Whip extends Decorator {
   public Whip(Beverage beverage) {
      super(beverage);
   }

   @Override
   public String getDescription() {
      return beverage.getDescription() + ", Whip";
   }

   @Override
   public double cost() {
      return beverage.cost() + 0.10;
   }
}
The original Beverage methods are overridden to refer to the Beverage owned as data member from the decorator.

Here is the code that create a plain espresso and a fancy espresso double mocha and whip:
Beverage b = new Espresso();
System.out.println(b.getDescription() + " €" + b.cost());

Beverage b2 = new Whip(new Mocha(new Mocha(new Espresso())));
System.out.println(b2.getDescription() + " €" + b2.cost());

No comments:

Post a Comment