 |
Attach additional responsibilities to an object dynamically. Decorators
provide a flexible alternative to subclassing for extending functionality.
Frequency of use: nbsp;medium
|
|
 |
The classes and/or objects participating in this pattern are:
- Component (LibraryItem)
- defines the interface for objects that can have responsibilities added to
them dynamically.
- ConcreteComponent (Book, Video)
- defines an object to which additional responsibilities can be attached.
- Decorator (Decorator)
- maintains a reference to a Component object and defines an interface that
conforms to Component's interface.
- ConcreteDecorator (Borrowable)
- adds responsibilities to the component.
This structural code demonstrates the Decorator pattern which dynamically adds
extra functionality to an existing object.
Show code
|
// Decorator pattern -- Structural example
|
|
using System;
namespace DoFactory.GangOfFour.Decorator.Structural
{
// MainApp test application
class MainApp
{
static void Main()
{
// Create ConcreteComponent and two Decorators
ConcreteComponent c = new ConcreteComponent();
ConcreteDecoratorA d1 = new ConcreteDecoratorA();
ConcreteDecoratorB d2 = new ConcreteDecoratorB();
// Link decorators
d1.SetComponent(c);
d2.SetComponent(d1);
d2.Operation();
// Wait for user
Console.Read();
}
}
// "Component"
abstract class Component
{
public abstract void Operation();
}
// "ConcreteComponent"
class ConcreteComponent : Component
{
public override void Operation()
{
Console.WriteLine("ConcreteComponent.Operation()");
}
}
// "Decorator"
abstract class Decorator : Component
{
protected Component component;
public void SetComponent(Component component)
{
this.component = component;
}
public override void Operation()
{
if (component != null)
{
component.Operation();
}
}
}
// "ConcreteDecoratorA"
class ConcreteDecoratorA : Decorator
{
private string addedState;
public override void Operation()
{
base.Operation();
addedState = "New State";
Console.WriteLine("ConcreteDecoratorA.Operation()");
}
}
// "ConcreteDecoratorB"
class ConcreteDecoratorB : Decorator
{
public override void Operation()
{
base.Operation();
AddedBehavior();
Console.WriteLine("ConcreteDecoratorB.Operation()");
}
void AddedBehavior()
{
}
}
}
|
Output
ConcreteComponent.Operation()
ConcreteDecoratorA.Operation()
ConcreteDecoratorB.Operation()
|
This real-world code demonstrates the Decorator pattern in which 'borrowable' functionality
is added to existing library items (books and videos).
Show code
|
// Decorator pattern -- Real World example
|
|
using System;
using System.Collections;
namespace DoFactory.GangOfFour.Decorator.RealWorld
{
// MainApp test application
class MainApp
{
static void Main()
{
// Create book
Book book = new Book ("Worley", "Inside ASP.NET", 10);
book.Display();
// Create video
Video video = new Video ("Spielberg", "Jaws", 23, 92);
video.Display();
// Make video borrowable, then borrow and display
Console.WriteLine("\nMaking video borrowable:");
Borrowable borrowvideo = new Borrowable(video);
borrowvideo.BorrowItem("Customer #1");
borrowvideo.BorrowItem("Customer #2");
borrowvideo.Display();
// Wait for user
Console.Read();
}
}
// "Component"
abstract class LibraryItem
{
private int numCopies;
// Property
public int NumCopies
{
get{ return numCopies; }
set{ numCopies = value; }
}
public abstract void Display();
}
// "ConcreteComponent"
class Book : LibraryItem
{
private string author;
private string title;
// Constructor
public Book(string author,string title,int numCopies)
{
this.author = author;
this.title = title;
this.NumCopies = numCopies;
}
public override void Display()
{
Console.WriteLine("\nBook ------ ");
Console.WriteLine(" Author: {0}", author);
Console.WriteLine(" Title: {0}", title);
Console.WriteLine(" # Copies: {0}", NumCopies);
}
}
// "ConcreteComponent"
class Video : LibraryItem
{
private string director;
private string title;
private int playTime;
// Constructor
public Video(string director, string title,
int numCopies, int playTime)
{
this.director = director;
this.title = title;
this.NumCopies = numCopies;
this.playTime = playTime;
}
public override void Display()
{
Console.WriteLine("\nVideo ----- ");
Console.WriteLine(" Director: {0}", director);
Console.WriteLine(" Title: {0}", title);
Console.WriteLine(" # Copies: {0}", NumCopies);
Console.WriteLine(" Playtime: {0}\n", playTime);
}
}
// "Decorator"
abstract class Decorator : LibraryItem
{
protected LibraryItem libraryItem;
// Constructor
public Decorator(LibraryItem libraryItem)
{
this.libraryItem = libraryItem;
}
public override void Display()
{
libraryItem.Display();
}
}
// "ConcreteDecorator"
class Borrowable : Decorator
{
protected ArrayList borrowers = new ArrayList();
// Constructor
public Borrowable(LibraryItem libraryItem)
: base(libraryItem)
{
}
public void BorrowItem(string name)
{
borrowers.Add(name);
libraryItem.NumCopies--;
}
public void ReturnItem(string name)
{
borrowers.Remove(name);
libraryItem.NumCopies++;
}
public override void Display()
{
base.Display();
foreach (string borrower in borrowers)
{
Console.WriteLine(" borrower: " + borrower);
}
}
}
}
|
Output
Book ------
Author: Worley
Title: Inside ASP.NET
# Copies: 10
Video -----
Director: Spielberg
Title: Jaws
# Copies: 23
Playtime: 92
Making video borrowable:
Video -----
Director: Spielberg
Title: Jaws
# Copies: 21
Playtime: 92
borrower: Customer #1
borrower: Customer #2
|
This .NET optimized code demonstrates the
same real-world situation as above but uses modern, built-in .NET features.
Show code
|
// Decorator pattern -- .NET optimized
|
See our Singleton page for a .NET optimized code sample.
|
|
|