-
Notifications
You must be signed in to change notification settings - Fork 3
Decorator Wrapper
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.
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.
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();