Skip to content
kgleong edited this page Oct 20, 2015 · 9 revisions

Motivation

The Decorator or Wrapper pattern allows functionality to be added to a base component without impacting how clients utilize the component.

Multiple Decorator types can wrap a base component or other Decorator instances, allowing a multitude of configurations without having to account for all possible configurations.

These Decorator objects act as skins on top of a base component.

Code

public interface Component {
    void performOperation();
}

The Decorator pattern has two main participants, both of which implement the Component interface:

  • concrete components - The primary building block that contains required functionality.
  • decorators - These classes wrap concrete components or other decorator instances and add optional functionality to the instances that they wrap.

By forcing Decorator and concrete component instances to adhere to the same contract, Decorator instances are transparent to clients, which only need to know that they hold references to Component objects.

public class ComponentImpl implements Component {
    public void performOperation() {
        System.out.println("Doing work in the base component");
    }
}

The ComponentImpl class represents the base component that implements required functionality.

public class DecoratorA implements Component {
    // Wrapped component
    Component mComponent;

    public DecoratorA(Component component) {
        mComponent = component;
    }

    public void performOperation() {
        System.out.println("Adding functionality with Decorator A");
        mComponent.performOperation();
    }
}

public class DecoratorB implements Component {
    Component mComponent;

    public DecoratorB(Component component) {
        mComponent = component;
    }

    public void performOperation() {
        mComponent.performOperation();
        System.out.println("Adding functionality with Decorator B");
    }
}

Both Decorator classes above wrap a Component instance that is provided at construction.

Within the performOperation() method, each Decorator can inject additional functionality either before or after the performOperation() call is passed through to the wrapped component.

Component baseComponent = new ComponentImpl();

Component decoratedComponent =
    new DecoratorB(new DecoratorA(baseComponent));

decoratedComponent.performOperation();

// Output:
// Adding functionality with Decorator A
// Doing work in the base component
// Adding functionality with Decorator B

This client code applies both DecoratorA and DecoratorB to the baseComponent instance.

The client is able to treat the decoratedComponent instance just as it would any other Component object.

The output shows that the DecoratorA and DecoratorB functionality have been added as expected.

Use Case

In a UI context, assume that a TextView class exists, which displays text to the screen.

In some scenarios, it may be required to add scroll bars and/or borders, but it's necessary to maintain the ability to choose to add these features or not to the TextView.

Using subclasses alone, this can become unwieldy and messy.

Using a ScrollBarViewDecorator and a BorderViewDecorator, however, the TextView can be treated as the base component while the Decorator classes can be added only if necessary.

// TextView extends/implements View.
// All View classes have a draw() method.
View textView = new TextView();

// View Decorators
View decoratedView =
    new ScrollBarDecoratorView(new BorderViewDecorator(textView));

// Render
decoratedView.draw();

References

Clone this wiki locally