Chain of Responsibility

Summary:  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, LINQ, lambda functions, and more. You can find an example on our Singleton pattern page.

All other patterns, and so much more, are available in our Dofactory .NET product.


Dofactory .NET includes the Gang of Four and Enterprise patterns, but also many other innovations including our Ultra-Clean™ Architecture, powerful low-code tactics, Rapid Application Development (RAD) techniques, and much more.

Accelerate your application development to where you can write entire solutions in just 33 days!. This unique package will change your outlook on development and your career.  Here's what is included:






Dofactory .NET
.NET Developer Pack




Learn More



  • 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
  • SparkTM Rapid App Dev (RAD) data access
  • Complete Analytics, Dashboard App
  • Complete Art Shop, Ecommerce App
  • Complete SaaS, Multi-Tenant App
  • Complete CRM, Customer Relationship App
  • 33-Day App Factory™
  • Everything 100% source code




Stay Inspired!
Join other developers and designers who have already signed up for our mailing list.
Terms     Privacy     Licensing       EULA       Sitemap      
© Data & Object Factory, LLC.
Made with    in Austin, Texas