ghytred.com


NewsLook for Outlook

Patterns and OO
DecoratorPattern
The Web Site you seek
Cannot be located, so
Waste your time here

Contents

Decorator

Fron GoF Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

Despite what the GoF book says, subclassing is a part of Decorator but is very much reduced. As an example in the diagram above, 'Component' could be Control, 'ConcreteComponent' could be TextBox, Decorator would be TextBoxDecorator, and the 'ContreteDecoratorX' classes might be BorderedTextBox, NumericTextBox, DateTextBox, FlashingTextBox, etc. The point of Decorator is that it allows you to combine the Concrete decorations without further subclassing: by combining the decorations you can get a bordered, flashing, numeric TextBox. Other possibilities are decorating a custom control to include a ListBox, a variety of CheckBoxes etc.

In the examples I have given above, there is no reason why the TextBoxDecorator should not inherit from TextBox, or even have the concrete decorators inherit from TextBox and do away with the TextBoxDecorator completely. A more general implementation would have decorator classes ControlDecorator, BorderedControlDecorator, etc. (The NumericTextBox and DateTextBox would not make sense in this scenario, as any control could be decorated by these classes, and it's only some controls where forcing input to be numeric or a date makes sense.)

As to the mechanics of the code, the ConcreteDecorator would take the object it is decorating as a parameter to its constructor. Note that it will have a common ancestor to this object, and so can treat this object in great part as the same type � almost � as itself. As an example, start with a class called Circle with the following interface (implementation is excluded for brevity):

public class Circle {
	public virtual Point Center {
		get;
		set;
	} 
	public virtual int Radius {
		get;
		set;
	}
	public virtual void Draw(Graphics g, Pen p){
		// draw the circle
	}
}

We can now define Decorators like:

public class BoxCircle : Circle{
	private Circle circle;
	public BoxCircle(Circle circle) {
		this.circle = circle;
	}
	public override Point Center {
		get { return circle.Center; }
		set { circle.Center = value; }
	} 
	public override int Radius {
		get { return circle.Radius; }
		set { circle.Radius = value; }
	}
	public override void Draw(Graphics g, Pen p){
		circle.Draw(g, p);
		Rectangle rect = new Rectangle(
			new Point(circle.Center.X - circle.Radius, 
				circle.Center.Y - circle.Radius),
			new Size(circle.Radius * 2, circle.Radius * 2)
			);
		g.DrawRectangle(p, rect);
	}
}

Usage would be as follows:

	Circle plain = new Circle();
	plain.Center = new Point(200, 200);
	plain.Radius = 20;
	BoxCircle boxed = new BoxCircle(plain);
	boxed.Draw(e.Graphics, pen);

We can also define say CrossCircle which will draw a cross in the middle of the circle. Usage would be exactly as above, but we can also combine the two concrete decorators to get a circle with a box drawn around it, and a cross drawn in the middle:

	Circle plain = new Circle();
	plain.Center = new Point(200, 200);
	plain.Radius = 20;
	CrossCircle crossedCircle = new CrossCircle(plain);
	BoxCircle boxNcross = new BoxCircle(crossedCircle);
	boxNcross.Draw(e.Graphics, pen);

This is the main point of this Pattern. It prevents an explosion of subclasses. We have one concrete class (Circle) and two decorators. This gives us a total of 4 possible styles; three decorators would give 8 styles; four decorators would give 15 styles; and so on. This is potentially a huge saving in complexity and maintenance costs.

As an extension of the example above, note that the boxed circle uses the base circle's size and draws its box to that size. If it enlarged itself and drew its box just outside the circle then it could be applied twice: this would draw a circle within a box within a box. Thus the same decorator can be applied multiple times.

In the canonical example � with an abstract Decorator inheriting from a superclass of the object to be decorated � the decorator is not coupled to the decoratee. In the circle example I have given, the decorators are coupled to the decoratee, which is not necessarily ideal; the boxing decorator could be used far more generally (it would have to use a different method to find the extent of the box to draw, but that's trivial), however drawing a cross right through the middle of many controls is probably not a good idea so it may be sensible to couple this decorator to something specific.

Documented used of Decorator I can find are nearly always visual (that is, in GUI Frameworks � as is my example). However, here are some that are not:

  • Smart Pointers in C++ are decorators which count references.
  • The .NET Framework has a number of classes which have a Synchronized method. This method returns a thread-safe version of the object. This appears to be done by decorators (it is in Java).
  • A Collection class can be decorated to sort the contents in various ways. For instance, a Hashtable has no direct way to either use foreach to iterate over its contents, nor to sort them. Decorators can be applied to do either of these and applied in series to do both.
  • Streams can be decorated to provide various pre- and post-processing operations such as compressing, converting to/from ASCII and Unicode, and so on.

Decorator should be used where an object has a number of optional extras, and these extras may be added ad-hoc in any combination.

Other points from http://www.stat.cmu.edu/~minka/patterns/Decorator.html:

  • A subject and its decorators are decoupled. The author of the subject does not need to do anything special for it to be decorated. Similarly, decoratees do not need to prepare for being decorated.
  • It is easy to add any combination of capabilities. The same capability can even be added twice. This is difficult with inheritance.
  • The same object may be simultaneously decorated in different ways. Clients can choose what capabilities they want by sending messages to the appropriate decorator.
  • Objects do not pay for capabilities they do not use. Thus we have efficiency and generality at the same time.
  • While a decorator has the same interface as its subject, it is not the same object. Hence object identity is not compatible with decorators. This also makes it hard to add a new decorator at run-time, since all client pointers must be changed. [svs: I am not sure of the validity of the 'all client pointers must be changed' comment; it may be language specific. The point that object identity is changed is a valid one and should be bourn in mind.]
  • Delegation may be required for self calls to work properly.
  • If the subject class is heavyweight, with lots of data or methods, it may make decorators too costly. Instead of changing the skin of the object, you can change the guts, via the Strategy pattern. Strategies do not have to conform to the subject's interface. The Strategy pattern can always replace the Decorator pattern, but it requires more anticipation. The Decorator pattern requires virtually no anticipation.
  • Sometimes objects need to call themselves or pass themselves to other objects. What should the subject do in this case? Should it pass itself or the decorators? If it should pass the decorators, then it needs to have some way of knowing about them. (Note that the Strategy pattern doesn't have this difficulty.) One way is delegation, where the decorator passes a reference to itself when it forwards the request. That way the subject knows who was the original recipient - but then the subject must know it is going to be decorated.

Comments drawn from GoF.

  • Adaptor provides a different interface to its subject. Proxy provides the same interface. Decorator provides an enhanced interface. [GoF, p216]
  • Adaptor changes an object's interface; Decorator enhances an object's responsibilities. Decorator is thus more transparent to the client. As a consequence, Decorator supports recursive composition, which isn't possible with pure Adapters. [GoF, p149]
  • Composite and Decorator have similar structure diagrams, reflecting the fact that both rely on recursive composition to organize an open-ended number of objects. [GoF, p219]
  • A Decorator can be viewed as a degenerate Composite with only one component. However, a Decorator adds additional responsibilities - it isn't intended for object aggregation. [GoF, p184]
  • Decorator is designed to let you add responsibilities to objects without subclassing. Composite's focus is not on embellishment but on representation. These intents are distinct but complementary. Consequently, Composite and Decorator are often used in concert. [GoF, p220]
  • Composite could use Chain of Responsibility to let components access global properties through their parent. It could also use Decorator to override these properties on parts of the composition. [GoF, p349]
  • Decorator and Proxy have different purposes but similar structures. Both describe how to provide a level of indirection to another object, and the implementations keep a reference to the object to which they forward requests. [GoF, p220]
  • Decorator lets you change the skin of an object. Strategy lets you change the guts. [GoF, p184]

Return to top