Lasagne/J and the Decorator Pattern |
The Decorator PatternThe Decorator Pattern is intended to “Attach additional responsibilities to an object dynamically”. ["Design Patterns - Elements of Reusable Software" by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides]. The motivating example of the Decorator
pattern is a graphical user interface toolkit, that lets you add properties
like borders or behaviors like scrolling to any user interface component by
enclosing the component in another objects that add the border or the scroll
capability. 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 allows you to nest
decorators recursively, thereby allowing an unlimited number of added
responsibilities.
In the Decorator pattern 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. Decorating is simply done by having the Decorator class forwarding draw requests to its component, and let the Decorator subclasses extend this operation. The figure below shows the class structure of the Decorator pattern.
Decorator subclasses can add additional operations for specific functionality. For instance, a ScrollDecorator can add a ScrollTo operation that lets other objects scroll the textview if they know there happens to be a ScrollDecorator object in the interface. An important aspect of the Decorator 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. |
Using Wrappers Instead of the Decorator PatternLasagne/J allows
a developer to decorate an object, without introducing the need for neither a
new class nor an additional object. The decorator code is dynamically
superimposed by a generic wrapper on the methods of the decorated object.
This superimposition allows a software designer to avoid the problems of
object identity, lack of common self, and Dopplegängers inherent in use of
the Decorator Pattern. |
Implementation A
decorator that adds a scrollbar to any subclass of VisualComponent can now be
defined as follows: package org.lasagnej.examples.designpatterns.decorator.scrollbar; wrapper ScrollDecorator wraps VisualComponent { public void draw() { System.out.println("ScrollDecorator::draw()"); inner.draw(); } } and
similar for a border decorator package org.lasagnej.examples.designpatterns.decorator.border; wrapper BorderDecorator wraps VisualComponent { public void draw() { System.out.println("BorderDecorator::draw()"); inner.draw(); } } If you want to apply a border around a TextView
object this is simply done with class widening. Context sensitive class
widening of an object is effectuated by constructive downcast of the object
reference through which the object is referenced in that particular context.
Repeating the class widening progress one more time on the same object
reference allows us to apply a border decorator as well. The source code
below exemplifies this procedure. public class Main { public static void main(String[] args) { TextView textView = new TextView(); textView.draw(); // let's have a text view with a scrollbar! textView = (TextView)((ScrollDecorator)textView); textView.draw(); // let's add a border as well! textView = (TextView)((BorderDecorator)textView); textView.draw(); } } |
Consequences Some of
the benefits and liabilities of using Lasagne/J when you need to decorate an
object are as follows:
|