.NET Design Patterns

Chain of Responsibility


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



Definition

Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.

Frequency of use:
Medium low




UML class diagram






Participants


    The classes and objects participating in this pattern are:

  • Handler   (Approver)
    • defines an interface for handling the requests
    • (optional) implements the successor link
  • ConcreteHandler   (Director, VicePresident, President)
    • handles requests it is responsible for
    • can access its successor
    • if the ConcreteHandler can handle the request, it does so; otherwise it forwards the request to its successor
  • Client   (ChainApp)
    • initiates the request to a ConcreteHandler object on the chain



Structural code in C#


This structural code demonstrates the Chain of Responsibility pattern in which several linked objects (the Chain) are offered the opportunity to respond to a request or hand it off to the object next in line.

                 

using System;

 

namespace DoFactory.GangOfFour.Chain.Structural

{

  /// <summary>

  /// MainApp startup class for Structural

  /// Chain of Responsibility Design Pattern.

  /// </summary>

  class MainApp

  {

    /// <summary>

    /// Entry point into console application.

    /// </summary>

    static void Main()

    {

      // Setup Chain of Responsibility

      Handler h1 = new ConcreteHandler1();

      Handler h2 = new ConcreteHandler2();

      Handler h3 = new ConcreteHandler3();

      h1.SetSuccessor(h2);

      h2.SetSuccessor(h3);

 

      // Generate and process request

      int[] requests = { 2, 5, 14, 22, 18, 3, 27, 20 };

 

      foreach (int request in requests)

      {

        h1.HandleRequest(request);

      }

 

      // Wait for user

      Console.ReadKey();

    }

  }

 

  /// <summary>

  /// The 'Handler' abstract class

  /// </summary>

  abstract class Handler

  {

    protected Handler successor;

 

    public void SetSuccessor(Handler successor)

    {

      this.successor = successor;

    }

 

    public abstract void HandleRequest(int request);

  }

 

  /// <summary>

  /// The 'ConcreteHandler1' class

  /// </summary>

  class ConcreteHandler1 : Handler

  {

    public override void HandleRequest(int request)

    {

      if (request >= 0 && request < 10)

      {

        Console.WriteLine("{0} handled request {1}",

          this.GetType().Name, request);

      }

      else if (successor != null)

      {

        successor.HandleRequest(request);

      }

    }

  }

 

  /// <summary>

  /// The 'ConcreteHandler2' class

  /// </summary>

  class ConcreteHandler2 : Handler

  {

    public override void HandleRequest(int request)

    {

      if (request >= 10 && request < 20)

      {

        Console.WriteLine("{0} handled request {1}",

          this.GetType().Name, request);

      }

      else if (successor != null)

      {

        successor.HandleRequest(request);

      }

    }

  }

 

  /// <summary>

  /// The 'ConcreteHandler3' class

  /// </summary>

  class ConcreteHandler3 : Handler

  {

    public override void HandleRequest(int request)

    {

      if (request >= 20 && request < 30)

      {

        Console.WriteLine("{0} handled request {1}",

          this.GetType().Name, request);

      }

      else if (successor != null)

      {

        successor.HandleRequest(request);

      }

    }

  }

}


Output
ConcreteHandler1 handled request 2
ConcreteHandler1 handled request 5
ConcreteHandler2 handled request 14
ConcreteHandler3 handled request 22
ConcreteHandler2 handled request 18
ConcreteHandler1 handled request 3
ConcreteHandler3 handled request 27
ConcreteHandler3 handled request 20




Real-world code in C#


This real-world code demonstrates the Chain of Responsibility pattern in which several linked managers and executives can respond to a purchase request or hand it off to a superior. Each position has can have its own set of rules which orders they can approve.

                 

using System;

 

namespace DoFactory.GangOfFour.Chain.RealWorld

{

  /// <summary>

  /// MainApp startup class for Real-World

  /// Chain of Responsibility Design Pattern.

  /// </summary>

  class MainApp

  {

    /// <summary>

    /// Entry point into console application.

    /// </summary>

    static void Main()

    {

      // Setup Chain of Responsibility

      Approver larry = new Director();

      Approver sam = new VicePresident();

      Approver tammy = new President();

 

      larry.SetSuccessor(sam);

      sam.SetSuccessor(tammy);

 

      // Generate and process purchase requests

      Purchase p = new Purchase(2034, 350.00, "Assets");

      larry.ProcessRequest(p);

 

      p = new Purchase(2035, 32590.10, "Project X");

      larry.ProcessRequest(p);

 

      p = new Purchase(2036, 122100.00, "Project Y");

      larry.ProcessRequest(p);

 

      // Wait for user

      Console.ReadKey();

    }

  }

 

  /// <summary>

  /// The 'Handler' abstract class

  /// </summary>

  abstract class Approver

  {

    protected Approver successor;

 

    public void SetSuccessor(Approver successor)

    {

      this.successor = successor;

    }

 

    public abstract void ProcessRequest(Purchase purchase);

  }

 

  /// <summary>

  /// The 'ConcreteHandler' class

  /// </summary>

  class Director : Approver

  {

    public override void ProcessRequest(Purchase purchase)

    {

      if (purchase.Amount < 10000.0)

      {

        Console.WriteLine("{0} approved request# {1}",

          this.GetType().Name, purchase.Number);

      }

      else if (successor != null)

      {

        successor.ProcessRequest(purchase);

      }

    }

  }

 

  /// <summary>

  /// The 'ConcreteHandler' class

  /// </summary>

  class VicePresident : Approver

  {

    public override void ProcessRequest(Purchase purchase)

    {

      if (purchase.Amount < 25000.0)

      {

        Console.WriteLine("{0} approved request# {1}",

          this.GetType().Name, purchase.Number);

      }

      else if (successor != null)

      {

        successor.ProcessRequest(purchase);

      }

    }

  }

 

  /// <summary>

  /// The 'ConcreteHandler' class

  /// </summary>

  class President : Approver

  {

    public override void ProcessRequest(Purchase purchase)

    {

      if (purchase.Amount < 100000.0)

      {

        Console.WriteLine("{0} approved request# {1}",

          this.GetType().Name, purchase.Number);

      }

      else

      {

        Console.WriteLine(

          "Request# {0} requires an executive meeting!",

          purchase.Number);

      }

    }

  }

 

  /// <summary>

  /// Class holding request details

  /// </summary>

  class Purchase

  {

    private int _number;

    private double _amount;

    private string _purpose;

 

    // Constructor

    public Purchase(int number, double amount, string purpose)

    {

      this._number = number;

      this._amount = amount;

      this._purpose = purpose;

    }

 

    // Gets or sets purchase number

    public int Number

    {

      get { return _number; }

      set { _number = value; }

    }

 

    // Gets or sets purchase amount

    public double Amount

    {

      get { return _amount; }

      set { _amount = value; }

    }

 

    // Gets or sets purchase purpose

    public string Purpose

    {

      get { return _purpose; }

      set { _purpose = value; }

    }

  }

}


Output
Director Larry approved request# 2034
President Tammy approved request# 2035
Request# 2036 requires an executive meeting!




.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