Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. They are also known as Wrappers.
Sometimes we want to add responsibilities to individual objects, not to an entire class. A graphical user interface toolkit, for example, should let you add properties like borders or behaviors like scrolling to any user interface component.
One way to add responsibilities is with inheritance. Inheriting a border from another class puts a border around every subclass instance. This is inflexible, however, because the choice of border is made statically. A client can't control how and when to decorate the component with a border.
A more flexible approach is to enclose the component in another object that adds the border. The enclosing object is called a decorator. The decorator conforms to the interface of the component it decorates so that its presence is transparent to the component's clients. The decorator forwards requests to the component and may perform additional actions (such as drawing a border) before or after forwarding. Transparency lets you nest decorators recursively, thereby allowing an unlimited number of added responsibilities.
For example, suppose we have a TextView object that displays text in a window. TextView has no scroll bars by default, because we might not always need them. When we do, we can use a ScrollDecorator to add them. Suppose we also want to add a thick black border around the TextView. We can use a BorderDecorator to add this as well. We simply compose the decorators with the TextView to produce the desired result.
The following object diagram shows how to compose a TextView object with BorderDecorator and ScrollDecorator objects to produce a bordered, scrollable text view:
The ScrollDecorator and BorderDecorator classes are subclasses of Decorator, an abstract class for visual components that decorate other visual components.
VisualComponent is the abstract class for visual objects. It defines their drawing and event handling interface. Note how the Decorator class simply forwards draw requests to its component, and how Decorator subclasses can extend this operation.
Decorator subclasses are free to add operations for specific functionality. For example, ScrollDecorator's ScrollTo operation lets other objects scroll the interface if they know there happens to be a ScrollDecorator object in the interface. The important aspect of this pattern is that it lets decorators appear anywhere a VisualComponent can. That way clients generally can't tell the difference between a decorated component and an undecorated one, and so they don't depend at all on the decoration.
The next step is to translate the domain definitions into a number of Elisa components. In addition, we will use skeletons of definitions as shown in the preceding class relationships.
Based on the domain definitions a set of related components are derived. The following component implements the the first domain definition VisualComponent = TextView | Decorator:
type Component = VisualComponent;
Draw(VisualComponent) -> Status;
<<implementations of >>
end component VisualComponents;
The following component is a skeleton example of a TextView component:
. . .
. . .
The following component is an example of a component for Decorator = ScrollDecorator | BorderDecorator:
Draw(Decorator) -> Status;
Draw(ScrollDecorator:scrollDecorator) = Draw(scrollDecorator);
Draw(BorderDecorator:borderDecorator) = Draw(borderDecorator);
. . .
end component Decorators;
The following component is a skeleton implementation of ScrollDecorator = . . . + scrollPosition:
ScrollTo(ScrollDecorator, Position=integer) -> nothing;
. . .
ScrollTo(scrolldecorator, position) = [ ... scrolldecorator.scrollPosition:= position ... ];
. . .
The following component is a skeleton implementation of BorderDecorator = . . . + borderWidth:
DrawBorder(BorderDecorator) -> nothing;
. . .
DrawBorder(borderdecorator) = [...Draw(borderdecorator.decorator); DrawBorder(borderdecorator)...];
. . .
This page was last modified on 04-12-2012 11:12:26