The Decorator Pattern
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.
Reference
Head First Design Patterns, the example above was taken from the book
Comments
Go to:
« previous entry: Project Mgmt 101
» next entry:
Top Five Fastest Growing IT Jobs
The Pragmatic Craftsman