Here's a simplified example of the decorator pattern. The class hierarchy is restructured as static
inner classes so that the whole example is contained in one compilation unit (as seen on ideone.com):
public class AnimalDecorator {
static abstract class Animal {
public abstract String makeNoise();
}
static class Dog extends Animal {
@Override public String makeNoise() { return "woof"; }
}
static class Cat extends Animal {
@Override public String makeNoise() { return "meow"; }
}
static class Normal extends Animal {
protected final Animal delegate;
Normal(Animal delegate) { this.delegate = delegate; }
@Override public String makeNoise() {
return delegate.makeNoise();
}
}
static class Loud extends Normal {
Loud(Animal delegate) { super(delegate); }
@Override public String makeNoise() {
return String.format("%S!!!", delegate.makeNoise());
}
}
static class Stuttering extends Normal {
Stuttering(Animal delegate) { super(delegate); }
@Override public String makeNoise() {
return delegate.makeNoise().replaceFirst(".", "$0-$0-$0-$0");
}
}
public static void keepPokingIt(Animal a) {
// let's skip the details for now...
System.out.println(a.makeNoise());
}
public static void main(String[] args) {
keepPokingIt(new Cat());
// meow
keepPokingIt(new Stuttering(new Dog()));
// w-w-w-woof
keepPokingIt(new Loud(new Cat()));
// MEOW!!!
keepPokingIt(new Loud(new Stuttering(new Dog())));
// W-W-W-WOOF!!!
}
}
So here we have a simple Animal
hierarchy, with Dog
and Cat
subclasses. We also have a Normal
decorator -- also an Animal
-- that simply delegates all methods to another Animal
. That is, it doesn't really do any effective decoration, but it's ready to be subclassed so that actual decorations can be added.
We only have one method here, makeNoise()
. We then have two kinds of actual decorations, Loud
and Stuttering
. (Consider the case where Animal
has many methods; then Normal
would be most valuable).
We then have a keepPokingIt(Animal)
method, which takes ANY Animal
, and would do unmentionable things to it until it makeNoise()
. In our main
function, we then keepPokingIt
various kinds of animals, decorated with various personality traits. Note that we can even stack one decoration on top of another.
The exact implementation details may vary, but this simplified example pretty much captures the essence of the decorator pattern.
Another example: ForwardingCollection
hierarchy from Guava
In the above example, keepPokingIt
only cares that it's an Animal
. Sometimes you may want to just poke a Cat
and not a Dog
, or in other ways differentiate the two types. In those kinds of scenarios, you'd then provide NormalCat
, NormalDog
, etc.
If you design your type hierarchy well, this should not be a problem. Remember that you don't have to write decorators for each implementation class
, but rather one for each type that you care about. Ideally, each type should even be an interface
rather than a concrete class
.
Consider the Java Collections Framework type hierarchy, for example. We have:
Guava conveniently facilitates decorator pattern implementations on top of this type hierarchy:
Note that there is no ForwardingHashMap<K,V>
, or a ForwardingTreeSet<E>
. There's probably no need for those anyway.
See also
- Effective Java 2nd Edition, Item 18: Prefer interfaces to abstract classes
Related questions