Introduction
The chain of responsibility pattern is a way of communication between objects.
As it’s name indicate a chain of handlers is built and every handler in the chain is responsible to handle the passing request or deliver it to the next handler in the chain. In the end of the process the request is handled with a default or exceptional behaviour.
The pattern helps to reduce coupling by freeing the object from knowing which handler will handle the request in the end.
Use Cases for the Chain of Responsibility Pattern
You should use the pattern in the following cases:
- You have more than one object that may handle a request.
- You have a scenario that you need to pass a request to one of several objects without specifying the receiver.
- You have handlers of a request that should be specified dynamically.
UML Diagram
Example in C#
#region Abstract Handler Class
public abstract class Handler
{
#region Properties
public int RequestLimit { get; private set; }
public Handler NextHandler { get; private set; }
#endregion
#region Methods
public abstract void HandleRequest(int request);
#endregion
#region Ctor
public Handler(Handler handler, int requestLimit)
{
NextHandler = handler;
RequestLimit = requestLimit;
}
#endregion
}
#endregion
#region Concrete Handlers
public class Worker : Handler
{
#region Ctor
public Worker(Handler handler)
: base(handler, 10000)
{
}
#endregion
#region Methods
public override void HandleRequest(int request)
{
if (request < RequestLimit)
{
Console.WriteLine("{0} handled a {1} request", GetType().Name, request);
}
else
{
if (NextHandler != null)
{
NextHandler.HandleRequest(request);
}
}
}
#endregion
}
public class Manager : Handler
{
#region Ctor
public Manager(Handler handler)
: base(handler, 20000)
{
}
#endregion
#region Methods
public override void HandleRequest(int request)
{
if (request < RequestLimit)
{
Console.WriteLine("{0} handled a {1} request", GetType().Name, request);
}
else
{
if (NextHandler != null)
{
NextHandler.HandleRequest(request);
}
}
}
#endregion
}
public class SeniorManager : Handler
{
#region Ctor
public SeniorManager(Handler handler)
: base(handler, 50000)
{
}
#endregion
#region Methods
public override void HandleRequest(int request)
{
if (request < RequestLimit)
{
Console.WriteLine("{0} handled a {1} request", GetType().Name, request);
}
else
{
if (NextHandler != null)
{
NextHandler.HandleRequest(request);
}
}
}
#endregion
}
#endregion
The example is simple.
I have three types of workers: worker, manager and a senior manager. Every worker type can handle request that are lower than the request limit they have.
Whenever a request that is bigger than the request limit arrives the worker type deliver the request to the next handler in the chain until there is no one who can handle the request.
The following example shows a use case of the example classes:
class Program
{
static void Main(string[] args)
{
SeniorManager seniorManager = new SeniorManager(null);
Manager manager = new Manager(seniorManager);
Worker worker = new Worker(manager);
worker.HandleRequest(5000);
worker.HandleRequest(15000);
worker.HandleRequest(35000);
Console.WriteLine();
manager.HandleRequest(5000);
manager.HandleRequest(15000);
manager.HandleRequest(35000);
Console.WriteLine();
seniorManager.HandleRequest(5000);
seniorManager.HandleRequest(15000);
seniorManager.HandleRequest(35000);
Console.Read();
}
}
Summary
To sum up, the chain of responsibility pattern isn’t commonly used. Even so it’s very helpful if you want to send a request along a chain of objects that may or may not handle the request.