- The Chain of Responsibility design pattern offers an opportunity to build a collection of loosely coupled objects by relieving a client from having to know which objects in a collection can satisfy a request by arranging these objects in a chain.
- It avoids 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.
- This pattern requires a way to order the search for an object that can handle the request.
- This search is usually modeled according to the specific needs of the application domain.
- Note that Chain-of-Responsibility is not commonly used in business application development.
- Occasionally you may run into a Chain of Responsibility implementation in which a chain of objects process a message between a sender and a receiver, in which each object does some processing on the message as it travels through the chain from the sender to the receiver.
- This is slightly different from the GoF definition in which just one object in a chain decides to handle the request.
Participants
Handler
(IApprover)
- 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.
var larry = new Director();
var sam = new VicePresident();
var tammy = new President();
larry.Successor = sam;
sam.Successor = tammy;
var purchase = new Purchase(2034, 350.00, "Assets");
larry.ProcessRequest(purchase);
purchase = new Purchase(2035, 32590.10, "Project X");
larry.ProcessRequest(purchase);
purchase = new Purchase(2036, 122100.00, "Project Y");
larry.ProcessRequest(purchase);
public interface IApprover
{
IApprover Successor { get; set; }
void ProcessRequest(Purchase purchase);
}
public class Director : IApprover
{
public IApprover Successor { get; set; }
public void ProcessRequest(Purchase purchase)
{
if (purchase.Amount < 10000.0)
Console.WriteLine("{0} approved request# {1}", GetType().Name, purchase.Number);
else if (Successor != null)
Successor.ProcessRequest(purchase);
}
}
public class VicePresident : IApprover
{
public IApprover Successor { get; set; }
public void ProcessRequest(Purchase purchase)
{
if (purchase.Amount < 25000.0)
Console.WriteLine("{0} approved request# {1}", GetType().Name, purchase.Number);
else if (Successor != null)
Successor.ProcessRequest(purchase);
}
}
public class President : IApprover
{
public IApprover Successor { get; set; }
public void ProcessRequest(Purchase purchase)
{
if (purchase.Amount < 100000.0)
Console.WriteLine("{0} approved request# {1}", GetType().Name, purchase.Number);
else Console.WriteLine("Request# {0} requires an executive meeting!", purchase.Number);
}
}
public class Purchase
{
public int Number { get; set; }
public double Amount { get; set; }
public string Purpose { get; set; }
public Purchase(int number, double amount, string purpose)
{
Number = number;
Amount = amount;
Purpose = purpose;
}
}
Rules of Thumb
- Chain of Responsibility, Command, Mediator, and Observer, address how you can decouple senders and receivers, but with different trade-offs.
- Chain of Responsibility passes a sender request along a chain of potential receivers.
- Chain of Responsibility can use Command to represent requests as objects.
- Chain of Responsibility is often applied in conjunction with Composite.
- There, a component’s parent can act as its successor.