The following code shows how to implement user interface decorators in C++. We'll assume there's a Component class called VisualComponent.
We define a subclass of VisualComponent called Decorator, which we'll subclass to obtain different decorations.class VisualComponent { public: VisualComponent(); virtual void Draw(); virtual void Resize(); // ... };
Decorator decorates the VisualComponent referenced by the _component instance variable, which is initialized in the constructor. For each operation in VisualComponent's interface, Decorator defines a default implementation that passes the request on to _component:class Decorator : public VisualComponent { public: Decorator(VisualComponent*); virtual void Draw(); virtual void Resize(); // ... private: VisualComponent* _component; };
Subclasses of Decorator define specific decorations. For example, the class BorderDecorator adds a border to its enclosing component. BorderDecorator is a subclass of Decorator that overrides the Draw operation to draw the border. BorderDecorator also defines a private DrawBorder helper operation that does the drawing. The subclass inherits all other operation implementations from Decorator.void Decorator::Draw () { _component->Draw(); } void Decorator::Resize () { _component->Resize(); }
A similar implementation would follow for ScrollDecorator and DropShadowDecorator, which would add scrolling and drop shadow capabilities to a visual component.class BorderDecorator : public Decorator { public: BorderDecorator(VisualComponent*, int borderWidth); virtual void Draw(); private: void DrawBorder(int); private: int _width; }; void BorderDecorator::Draw () { Decorator::Draw(); DrawBorder(_width); }
Now we can compose instances of these classes to provide different decorations. The following code illustrates how we can use decorators to create a bordered scrollable TextView.
First, we need a way to put a visual component into a window object. We'll assume our Window class provides a SetContents operation for this purpose:
Now we can create the text view and a window to put it in:void Window::SetContents (VisualComponent* contents) { // ... }
TextView is a VisualComponent, which lets us put it into the window:Window* window = new Window; TextView* textView = new TextView;
But we want a bordered and scrollable TextView. So we decorate it accordingly before putting it in the window.window->SetContents(textView);
Because Window accesses its contents through the VisualComponent interface, it's unaware of the decorator's presence. You, as the client, can still keep track of the text view if you have to interact with it directly, for example, when you need to invoke operations that aren't part of the VisualComponent interface. Clients that rely on the component's identity should refer to it directly as well.window->SetContents( new BorderDecorator( new ScrollDecorator(textView), 1 ) );