Decorator
The decorator pattern
The decorator pattern is a structural pattern. The benefit is to adorn another class to broaden its functionality. The example demonstrates this 'recursively'. It's complicated.First an abstract VisualComponent:
abstract class VisualComponent {
public virtual void Draw() {
Console.WriteLine("Drawing " + this.GetType().Name);
}
}
A nothing implementation:
public virtual void Draw() {
Console.WriteLine("Drawing " + this.GetType().Name);
}
}
class TextView : VisualComponent {
}
}
A quick console test of TextView:
static void Main(string[] args) {
VisualComponent textView = new TextView();
textView.Draw();
Console.ReadKey();
}
Output:
VisualComponent textView = new TextView();
textView.Draw();
Console.ReadKey();
}
Drawing TextView
Cool code complete!New specifications land. The (production)TextView requires a border!
What to do? ...
Build a decorator! Disturbing only the composite root.
The general decorator:
class VisualComponentDecorator : VisualComponent {
protected VisualComponent visualComponent;
public VisualComponentDecorator(VisualComponent visualComponent) {
this.visualComponent = visualComponent;
}
public override void Draw() {
visualComponent.Draw();
}
}
The decorator (above) inherits from abstract class VisualComponent.
Also taking the abstract class as a dependency.protected VisualComponent visualComponent;
public VisualComponentDecorator(VisualComponent visualComponent) {
this.visualComponent = visualComponent;
}
public override void Draw() {
visualComponent.Draw();
}
}
The new Border decorator, implementation:
class BorderVisualComponentDecorator : VisualComponentDecorator {
private int borderWidth;
public BorderVisualComponentDecorator(VisualComponent visualComponent, int borderWidth) : base(visualComponent) {
this.borderWidth = borderWidth;
}
public override void Draw() {
visualComponent.Draw();
DrawBorder(borderWidth);
}
private void DrawBorder(int borderWidth) {
Console.WriteLine("Drawing border with width {0}.", borderWidth);
}
}
Adjusting the root:
private int borderWidth;
public BorderVisualComponentDecorator(VisualComponent visualComponent, int borderWidth) : base(visualComponent) {
this.borderWidth = borderWidth;
}
public override void Draw() {
visualComponent.Draw();
DrawBorder(borderWidth);
}
private void DrawBorder(int borderWidth) {
Console.WriteLine("Drawing border with width {0}.", borderWidth);
}
}
static void Main(string[] args) {
VisualComponent textView = new TextView();
BorderVisualComponentDecorator border = new BorderVisualComponentDecorator(textView, 3);
border.Draw();
Console.ReadKey();
}
The output:
VisualComponent textView = new TextView();
BorderVisualComponentDecorator border = new BorderVisualComponentDecorator(textView, 3);
border.Draw();
Console.ReadKey();
}
Drawing TextView
Drawing border with width 3.
Drawing border with width 3.
Code complete! Time to hit the pub/gym/club...
Later, the testers complain they can't see stuff at the bottom of the page.
A scrollbar is required.
No hassle!
Build a scrollbar decorator for the border decorator:
class ScrollVisualComponentDecorator : VisualComponentDecorator {
public ScrollVisualComponentDecorator(VisualComponent visualComponent) : base(visualComponent) { }
public override void Draw() {
visualComponent.Draw();
AdornScrollbar();
}
private void AdornScrollbar() {
Console.WriteLine("Adorning scrollbar.");
}
}
Changing the corresponding root code:
public ScrollVisualComponentDecorator(VisualComponent visualComponent) : base(visualComponent) { }
public override void Draw() {
visualComponent.Draw();
AdornScrollbar();
}
private void AdornScrollbar() {
Console.WriteLine("Adorning scrollbar.");
}
}
static void Main(string[] args) {
VisualComponent textView = new TextView();
VisualComponent scrollbar = new ScrollVisualComponentDecorator(textView);
BorderVisualComponentDecorator border = new BorderVisualComponentDecorator(scrollbar, 3);
border.Draw();
Console.ReadKey();
}
Now the output is:
VisualComponent textView = new TextView();
VisualComponent scrollbar = new ScrollVisualComponentDecorator(textView);
BorderVisualComponentDecorator border = new BorderVisualComponentDecorator(scrollbar, 3);
border.Draw();
Console.ReadKey();
}
Drawing TextView
Adorning scrollbar.
Drawing border with width 3.
Adorning scrollbar.
Drawing border with width 3.
Summary
The decorator pattern is
complicated.
Maybe feeding one instance to another can cause confusion.