DesignPattern Tutorials

Decorator Design Pattern

Definition

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

Frequency of use:
Medium




UML class diagram






Participants


    The classes and 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.



Structural code in C#


This structural code demonstrates the Decorator pattern which dynamically adds extra functionality to an existing object.

  1. using System;

  2.  

  3. namespace DoFactory.GangOfFour.Decorator.Structural

  4. {

  5.   /// <summary>

  6.   /// MainApp startup class for Structural

  7.   /// Decorator Design Pattern.

  8.   /// </summary>

  9.   class MainApp

  10.   {

  11.     /// <summary>

  12.     /// Entry point into console application.

  13.     /// </summary>

  14.     static void Main()

  15.     {

  16.       // Create ConcreteComponent and two Decorators

  17.       ConcreteComponent c = new ConcreteComponent();

  18.       ConcreteDecoratorA d1 = new ConcreteDecoratorA();

  19.       ConcreteDecoratorB d2 = new ConcreteDecoratorB();

  20.  

  21.       // Link decorators

  22.       d1.SetComponent(c);

  23.       d2.SetComponent(d1);

  24.  

  25.       d2.Operation();

  26.  

  27.       // Wait for user

  28.       Console.ReadKey();

  29.     }

  30.   }

  31.  

  32.   /// <summary>

  33.   /// The 'Component' abstract class

  34.   /// </summary>

  35.   abstract class Component

  36.   {

  37.     public abstract void Operation();

  38.   }

  39.  

  40.   /// <summary>

  41.   /// The 'ConcreteComponent' class

  42.   /// </summary>

  43.   class ConcreteComponent : Component

  44.   {

  45.     public override void Operation()

  46.     {

  47.       Console.WriteLine("ConcreteComponent.Operation()");

  48.     }

  49.   }

  50.  

  51.   /// <summary>

  52.   /// The 'Decorator' abstract class

  53.   /// </summary>

  54.   abstract class Decorator : Component

  55.   {

  56.     protected Component component;

  57.  

  58.     public void SetComponent(Component component)

  59.     {

  60.       this.component = component;

  61.     }

  62.  

  63.     public override void Operation()

  64.     {

  65.       if (component != null)

  66.       {

  67.         component.Operation();

  68.       }

  69.     }

  70.   }

  71.  

  72.   /// <summary>

  73.   /// The 'ConcreteDecoratorA' class

  74.   /// </summary>

  75.   class ConcreteDecoratorA : Decorator

  76.   {

  77.     public override void Operation()

  78.     {

  79.       base.Operation();

  80.       Console.WriteLine("ConcreteDecoratorA.Operation()");

  81.     }

  82.   }

  83.  

  84.   /// <summary>

  85.   /// The 'ConcreteDecoratorB' class

  86.   /// </summary>

  87.   class ConcreteDecoratorB : Decorator

  88.   {

  89.     public override void Operation()

  90.     {

  91.       base.Operation();

  92.       AddedBehavior();

  93.       Console.WriteLine("ConcreteDecoratorB.Operation()");

  94.     }

  95.  

  96.     void AddedBehavior()

  97.     {

  98.     }

  99.   }

  100. }

  101.  
  102.  

Output
ConcreteComponent.Operation()
ConcreteDecoratorA.Operation()
ConcreteDecoratorB.Operation()




Real-world code in C#


This real-world code demonstrates the Decorator pattern in which 'borrowable' functionality is added to existing library items (books and videos).

  1. using System;

  2. using System.Collections.Generic;

  3.  

  4. namespace DoFactory.GangOfFour.Decorator.RealWorld

  5. {

  6.   /// <summary>

  7.   /// MainApp startup class for Real-World

  8.   /// Decorator Design Pattern.

  9.   /// </summary>

  10.   class MainApp

  11.   {

  12.     /// <summary>

  13.     /// Entry point into console application.

  14.     /// </summary>

  15.     static void Main()

  16.     {

  17.       // Create book

  18.       Book book = new Book("Worley", "Inside ASP.NET", 10);

  19.       book.Display();

  20.  

  21.       // Create video

  22.       Video video = new Video("Spielberg", "Jaws", 23, 92);

  23.       video.Display();

  24.  

  25.       // Make video borrowable, then borrow and display

  26.       Console.WriteLine("\nMaking video borrowable:");

  27.  

  28.       Borrowable borrowvideo = new Borrowable(video);

  29.       borrowvideo.BorrowItem("Customer #1");

  30.       borrowvideo.BorrowItem("Customer #2");

  31.  

  32.       borrowvideo.Display();

  33.  

  34.       // Wait for user

  35.       Console.ReadKey();

  36.     }

  37.   }

  38.  

  39.   /// <summary>

  40.   /// The 'Component' abstract class

  41.   /// </summary>

  42.   abstract class LibraryItem

  43.   {

  44.     private int _numCopies;

  45.  

  46.     // Property

  47.     public int NumCopies

  48.     {

  49.       get { return _numCopies; }

  50.       set { _numCopies = value; }

  51.     }

  52.  

  53.     public abstract void Display();

  54.   }

  55.  

  56.   /// <summary>

  57.   /// The 'ConcreteComponent' class

  58.   /// </summary>

  59.   class Book : LibraryItem

  60.   {

  61.     private string _author;

  62.     private string _title;

  63.  

  64.     // Constructor

  65.     public Book(string author, string title, int numCopies)

  66.     {

  67.       this._author = author;

  68.       this._title = title;

  69.       this.NumCopies = numCopies;

  70.     }

  71.  

  72.     public override void Display()

  73.     {

  74.       Console.WriteLine("\nBook ------ ");

  75.       Console.WriteLine(" Author: {0}", _author);

  76.       Console.WriteLine(" Title: {0}", _title);

  77.       Console.WriteLine(" # Copies: {0}", NumCopies);

  78.     }

  79.   }

  80.  

  81.   /// <summary>

  82.   /// The 'ConcreteComponent' class

  83.   /// </summary>

  84.   class Video : LibraryItem

  85.   {

  86.     private string _director;

  87.     private string _title;

  88.     private int _playTime;

  89.  

  90.     // Constructor

  91.     public Video(string director, string title,

  92.       int numCopies, int playTime)

  93.     {

  94.       this._director = director;

  95.       this._title = title;

  96.       this.NumCopies = numCopies;

  97.       this._playTime = playTime;

  98.     }

  99.  

  100.     public override void Display()

  101.     {

  102.       Console.WriteLine("\nVideo ----- ");

  103.       Console.WriteLine(" Director: {0}", _director);

  104.       Console.WriteLine(" Title: {0}", _title);

  105.       Console.WriteLine(" # Copies: {0}", NumCopies);

  106.       Console.WriteLine(" Playtime: {0}\n", _playTime);

  107.     }

  108.   }

  109.  

  110.   /// <summary>

  111.   /// The 'Decorator' abstract class

  112.   /// </summary>

  113.   abstract class Decorator : LibraryItem

  114.   {

  115.     protected LibraryItem libraryItem;

  116.  

  117.     // Constructor

  118.     public Decorator(LibraryItem libraryItem)

  119.     {

  120.       this.libraryItem = libraryItem;

  121.     }

  122.  

  123.     public override void Display()

  124.     {

  125.       libraryItem.Display();

  126.     }

  127.   }

  128.  

  129.   /// <summary>

  130.   /// The 'ConcreteDecorator' class

  131.   /// </summary>

  132.   class Borrowable : Decorator

  133.   {

  134.     protected List<string> borrowers = new List<string>();

  135.  

  136.     // Constructor

  137.     public Borrowable(LibraryItem libraryItem)

  138.       : base(libraryItem)

  139.     {

  140.     }

  141.  

  142.     public void BorrowItem(string name)

  143.     {

  144.       borrowers.Add(name);

  145.       libraryItem.NumCopies--;

  146.     }

  147.  

  148.     public void ReturnItem(string name)

  149.     {

  150.       borrowers.Remove(name);

  151.       libraryItem.NumCopies++;

  152.     }

  153.  

  154.     public override void Display()

  155.     {

  156.       base.Display();

  157.  

  158.       foreach (string borrower in borrowers)

  159.       {

  160.         Console.WriteLine(" borrower: " + borrower);

  161.       }

  162.     }

  163.   }

  164. }

  165.  
  166.  

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

;