.NET Design Patterns

Decorator


 Definition
 UML diagram
 Participants
 Structural code in C#
 Real-world code in C#
 .NET Optimized code in C#



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.

                 

using System;

 

namespace DoFactory.GangOfFour.Decorator.Structural

{

  /// <summary>

  /// MainApp startup class for Structural

  /// Decorator Design Pattern.

  /// </summary>

  class MainApp

  {

    /// <summary>

    /// Entry point into console application.

    /// </summary>

    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.ReadKey();

    }

  }

 

  /// <summary>

  /// The 'Component' abstract class

  /// </summary>

  abstract class Component

  {

    public abstract void Operation();

  }

 

  /// <summary>

  /// The 'ConcreteComponent' class

  /// </summary>

  class ConcreteComponent : Component

  {

    public override void Operation()

    {

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

    }

  }

 

  /// <summary>

  /// The 'Decorator' abstract class

  /// </summary>

  abstract class Decorator : Component

  {

    protected Component component;

 

    public void SetComponent(Component component)

    {

      this.component = component;

    }

 

    public override void Operation()

    {

      if (component != null)

      {

        component.Operation();

      }

    }

  }

 

  /// <summary>

  /// The 'ConcreteDecoratorA' class

  /// </summary>

  class ConcreteDecoratorA : Decorator

  {

    public override void Operation()

    {

      base.Operation();

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

    }

  }

 

  /// <summary>

  /// The 'ConcreteDecoratorB' class

  /// </summary>

  class ConcreteDecoratorB : Decorator

  {

    public override void Operation()

    {

      base.Operation();

      AddedBehavior();

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

    }

 

    void AddedBehavior()

    {

    }

  }

}


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).

                 

using System;

using System.Collections.Generic;

 

namespace DoFactory.GangOfFour.Decorator.RealWorld

{

  /// <summary>

  /// MainApp startup class for Real-World

  /// Decorator Design Pattern.

  /// </summary>

  class MainApp

  {

    /// <summary>

    /// Entry point into console application.

    /// </summary>

    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.ReadKey();

    }

  }

 

  /// <summary>

  /// The 'Component' abstract class

  /// </summary>

  abstract class LibraryItem

  {

    private int _numCopies;

 

    // Property

    public int NumCopies

    {

      get { return _numCopies; }

      set { _numCopies = value; }

    }

 

    public abstract void Display();

  }

 

  /// <summary>

  /// The 'ConcreteComponent' class

  /// </summary>

  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);

    }

  }

 

  /// <summary>

  /// The 'ConcreteComponent' class

  /// </summary>

  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);

    }

  }

 

  /// <summary>

  /// The 'Decorator' abstract class

  /// </summary>

  abstract class Decorator : LibraryItem

  {

    protected LibraryItem libraryItem;

 

    // Constructor

    public Decorator(LibraryItem libraryItem)

    {

      this.libraryItem = libraryItem;

    }

 

    public override void Display()

    {

      libraryItem.Display();

    }

  }

 

  /// <summary>

  /// The 'ConcreteDecorator' class

  /// </summary>

  class Borrowable : Decorator

  {

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

 

    // 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




.NET Optimized code in C#


The .NET optimized code demonstrates the same real-world situation as above but uses modern, built-in .NET features, such as, generics, reflection, object initializers, automatic properties, etc. You can find an example on our Singleton pattern page.

All other patterns (and much more) are available in our .NET Design Pattern Framework 4.5.


Not only does the .NET Design Pattern Framework 4.5 cover GOF and Enterprise patterns, it also includes .NET pattern architectures that reduce the code you need to write by up to 75%. This unique package will change your .NET lifestyle -- for only $79.  Here's what is included:



  • 69 gang-of-four pattern projects
  • 46 head-first pattern projects
  • Fowler's enterprise patterns
  • Multi-tier patterns
  • Convention over configuration
  • Active Record and CQRS patterns
  • Repository and Unit-of-Work patterns
  • MVC, MVP, & MVVM patterns
  • REST patterns with Web API
  • SparkTM Rapid App Dev (RAD) platform!
  • Art Shop MVC Reference Application
  • 100% pure source code