Decorator pattern: decorators still need to be reused

Russian Matroshka

An inherent concern of OO design: how can we extent a class without modifying its content? If we can do that, we satisfy the Open/Closed Principle (the O in SOLID).

Let’s take an example, a class Rectangle with a method draw:

class Rectangle {
  public void draw() {
    // Render a red rectangle
  }
}

Assume that there’s a client which is using the Rectangle class. The client code is stable, it will depend on Rectangle permanently.

Now we want to draw a Vietnamese flag that contains a rectangle and a star. Of course, we must reuse the Rectangle class and extent it in order to add a star. There are two common ways to achieve this: Inheritance and Aggregation.

Inheritance

Let a new class VietnameseFlag inherits Rectangle:

class VietnameseFlag extends Rectangle {
  public void draw() {
    super.draw(); // render a red rectangle
    // and then render a star
  }
}

That’s cool! Now we just create a new instance of VietnameseFlag and pass it to the client. Using inheritance is very convenient, but the code drawing a star cannot be reused. A VietnameseFlag object always draws a star on a red rectangle because it inherits Rectangle. As a result, the code drawing a star cannot be reused for other purposes e.g. drawing a star on a blue rectangle.

Aggregation

A VietnameseFlag object should not inherit a Rectangle object but should decorate the Rectangle object with a star.

class VietnameseFlag {
  private Rectangle rect;
  public VietnameseFlag(Rectangle rect) {
    this.rect = rect;
  }
  public void draw() {
    this.rect.draw(); // render a rectangle
    // decorate the rectangle by rendering a star
  }
}

Rectangle flag = new VietnameseFlag(new Rectangle());
// pass the flag object to client

VietnameseFlag now contains a reference to Rectangle and this reference is passed into the constructor of VietnameseFlag. This relationship is called aggregation and is done through Dependency Injection.

Opps! We cannot pass the flag object to our client because this object does not belong to Rectangle type. To fix this:

  • change Rectangle to become an interface
  • create a new class named RedRectangle which implements Rectangle
  • let RedRectangle has the same content with the old Rectangle class
  • let VietnameseFlag implements Rectangle. Note that VietnameseFlag will contain a reference to interface Rectangle, not class RedRectangle. Remember the D of SOLID?
interface Rectangle {
  void draw();
}

class RedRectangle implements Rectangle {
  public void draw() {
    // render a red rectangle
  }
}

class VietnameseFlag implements Rectangle {
  private Rectangle rect;
  public VietnameseFlag(Rectangle rect) {
    this.rect = rect;
  }
  public void draw() {
    this.rect.draw(); // render a rectangle,
                      // don't care its color
    // then decorate the rectangle by rendering a star
  }
}

Rectangle flag = new VietnameseFlag(new RedRectangle());
// now we can pass the flag object to client

This approach is very flexible and composable. To draw a star on a blue rectangle, just create a new class named BlueRectangle implementing Rectangle, and then compose a VietnameseFlag object with a BlueRectangle object:

class BlueRectangle implements Rectangle {
  public void draw() {
    // Render a blue rectangle
  }
}

VietnameseFlag flag = new VietnameseFlag(new BlueRectangle());

In fact, VietnameseFlag decorates a star on a rectangle. Hence, its name should expose the decorator job, for example, we might change its name to StarredRectangle or RectangleWithStar.

When we have many derivations of Rectangle and many complex ways to compose them, we will see the power of Decorator pattern and the great of Gang of Four.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s