Thursday, November 29, 2012

Understanding the Decorator pattern

For those of you who struggle with design patterns, I hope the following steps can help you understand the Decorator pattern and why it can be useful.

Step 1: Understand its purpose

The Decorator pattern is all about extending a class, without actually extending (inheriting from) it. If you still wonder why inheritance isn't always your best choice to add functionality to a class, read the following points:
  • With inheritance, you add functionality to just one class: the one you inherit from. If you want to add the same functionality to more than one class, then inheritance is not the way to go.
  • Inheritance is static. If you want to dynamically add functionality to objects at runtime, then inheritance is not the way to go.
  • If you inherit from a class, you are dependent on the implementation of your superclass. It's not too hard for changes in a superclass to break a subclass.
If none of these points pose a problem for your particular application, then inheritance might just be the right tool for the job. Otherwise, do read on.

Step 2: Start with the basics

So how do you extend a class without inheriting from it ? Well, it's easy: just create a new class and give that class an attribute of the old class. This new class can add whatever functionality it wants to and use the methods of the old class when it needs to.

A basic design might be as follows:

Here, class MyDecorator has an attribute of type MyClass. The implementation of MyDecorator might be as follows:

public class MyDecorator {

  private MyClass myClass;

  public MyDecorator(MyClass myClass) {
    this.myClass = myClass;

  public void doSomething() {
    // add some functionality
    // ...

    // delegate something to myClass

    // add some more functionality
    // ...

This basic design allows you to dynamically extend the functionality of an object of type MyClass by wrapping it with an object of type MyDecorator when needed. In this basic design, composition and delegation have replaced inheritance.

Step 3: Add some polymorphism

The design in step 2 got rid of inheritance but still lacks some of the benefits of a full Decorator pattern. For example: we're still not able to add the same functionality to more than one class. To achieve this, we modify our design as follows:

We introduce an interface MyInterface and give MyDecorator an attribute of this type, instead of a concrete subtype. Now the functionality of MyDecorator can be added to every class that implements MyInterface.

Step 4: Take it all the way

Even though we already achieved the desired results in step 3, the Decorator pattern has one more trick up its sleeve. That trick is to make your decorator classes and the classes they decorate have a common supertype. In our example, we simply make MyDecorator implement MyInterface as well: 

This technique has two major benefits:
  1. Because decorators and regular classes have the same supertype, you can use a decorated object wherever you'd use an undecorated one.
  2. Because decorators and regular classes have the same supertype, the objects that get decorated, can be decorated objects themselves. As a result, you can chain decorators together and decorate an object with as many decorators as you like.
Tada !

I hope you learned something from all this, and will remember it whenever you start using inheritance for something it's not suited for. Just please don't start replacing every inheritance hierarchy with a chain of decorators. If you do, you might just be suffering from pattern fever.

For more information, I can recommend the following reads:
  • Head First Design Patterns (Freeman & Freeman), Chapter 3
  • Effective Java, 2nd ed. (Bloch), Item 16

No comments:

Post a Comment