Sunday, March 16, 2008

The Decorator Pattern

From http://pragmaticcraftsman.com/design_patterns/

I've been in the dark as far as the Decorator pattern is concerned. I knew, in principle, how it works, but I can see that my understanding was very incomplete. Plus, I never had a chance to use it. While reading the Head First Design Patterns book, I discovered something basic that: when you wrap an object several times and then call a method on it, it will be called as many times as it was wrapped. The trick? Keep a reference to the object -- you're creating a chain.

Let me go through this step by step. By example. This is the book's example, the Starbuzz Coffee decorator.

A simple interface.

public interface Beverage {

public String getDescription();

public BigDecimal getCost();

}

We have several coffee types, Dark Roast being one of them.

public class DarkRoast implements Beverage {

public BigDecimal getCost() {

return new BigDecimal("1.55");

public String getDescription() {

return "Dark Roast";

}

}

When ordering coffee, you can pick a coffee type (Dark Roast, Latte, etc), and you can also add on to the coffee. For instance, you can add a whip cream on top, or add a shot of expresso to it. Which, of course, adds to the price. You could extend Beverage with all of the types, but that's too many classes. Here's where the decorator pattern comes into play.

Here's the solution. You define the AddOnDecorator and extend it with the different add ons.

/** It's just an empty class */

public abstract class AddOnDecorator implements Beverage { }

public class Expresso extends AddOnDecorator {

Beverage beverage;

public Expresso(Beverage beverage) {

this.beverage = beverage;

}

public BigDecimal getCost() {

return new BigDecimal("0.25").add(beverage.getCost());

}

public String getDescription() {

return beverage.getDescription() + ", with Expresso";

}

public class Whip extends AddOnDecorator {

Beverage beverage;

public Whip(Beverage beverage) {

this.beverage = beverage;

public BigDecimal getCost() {

return new BigDecimal("0.10").add(beverage.getCost());

public String getDescription() {

return beverage.getDescription() + ", Whipped";

}

}

public class StarbuzzCoffee {

public static void main(String[] args) {

Beverage darkRoast = new DarkRoast();

darkRoast = new Expresso(darkRoast);

darkRoast = new Whip(darkRoast);

System.out.println(darkRoast.getDescription() + " costs "

+ darkRoast.getCost());

}

}

When I first looked at the code, I was confused. I thought that new Whip(...) would just override it, right?

This is what gets printed: Dark Roast, with Expresso, Whipped costs 1.90

You get the beverage you want (DarkRoast), you pass it to the Expresso wrapper, which in turn passes it to the Whip wrapper.

First the Expresso wrapper is called, it receives the dark roast beverage. Then the dark roast is passed again to the Whip wrapper. If you look closely (and this is a little confusing), when an Expresso is instantiated, it receives the beverage and makes a local copy (so it is never lost). Essentially, a chain is made. When the action on the final object is made, the chain is traversed and the beverage object is passed around. That's how this pattern works. (Wow, I learn something (basic) every day. :-))

The Decorator pattern is cool. It rocks. :-) It lets you keep adding functionality and still keep the objects cohesive (as you're not bloating the objects). No coupling as well. Nice.

0 comments: