| Type | Behavioral |
| Purpose | 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. |
Description
The Chain of Responsibility pattern organises processing as a sequence of handler objects, each of which is given an opportunity to deal with a request. If a handler cannot process the request, it passes it to the next handler in the chain. At the end of the chain, the request may either remain unhandled, trigger default behaviour, or result in an exception, depending on the design.
This arrangement makes it easier to vary responsibilities dynamically and to compose processing logic from multiple independent steps. Each handler contains the logic needed to determine whether it can handle a given request and, if not, how to forward that request to its successor.
Chain of Responsibility is commonly used in request-processing pipelines, where work is distributed across a sequence of components rather than concentrated in a single object. This supports a flexible and extensible architecture by decoupling the sender of a request from the objects that may handle it.
The pattern is especially powerful because each handler is concerned only with its own part of the processing and with forwarding the request when appropriate; it does not need to know the full details of the rest of the chain.
In ASP.NET Core, middleware components form a request and response pipeline in which each middleware component can process the request and then invoke the next component in the chain.
Chain of Responsibility is primarily concerned with passing a request along a chain of potential handlers.
Overview
| Client | The Client initiates the request to a chain of handler objects. |
| Handler | Defines the operation for handling requests and, where appropriate, provides a way to forward a request to the next handler in the chain. |
| ConcreteHandlers | These are individual handlers implementing the Handler interface that handle the requests they are responsible for. |
Implementation
In this example, authors of several computer programming languages are seated at a table. A book is passed to the first author for signing. If the book is related to his language, he will sign it, otherwise he will pass it to the next author. If no authors have signed it, then a default author will sign the book.
// Handler
public abstract class LanguageCreator
{
protected LanguageCreator? Successor;
public void SetSuccessor(LanguageCreator successor)
=> Successor = successor;
public abstract void SignBook(string language);
}
// ConcreteHandlers
public class BjarneStroustrup : LanguageCreator
{
public override void SignBook(string language)
{
if (language == "C++")
{
Console.WriteLine($"{GetType().Name} signs the {language} book.");
}
else
{
Successor?.SignBook(language);
}
}
}
public class JamesGosling : LanguageCreator
{
public override void SignBook(string language)
{
if (language == "Java")
{
Console.WriteLine($"{GetType().Name} signs the {language} book.");
}
else
{
Successor?.SignBook(language);
}
}
}
public class GuidoVanRossum : LanguageCreator
{
public override void SignBook(string language)
{
if (language == "Python")
{
Console.WriteLine($"{GetType().Name} signs the {language} book.");
}
else
{
Successor?.SignBook(language);
}
}
}
public class JonathanBlow : LanguageCreator
{
public override void SignBook(string language) =>
Console.WriteLine($"{language} author not present, but {GetType().Name} signs!");
}
// Client
public static class ChainOfResponsibilityDemo
{
public static void Execute()
{
// create our handlers
var cppCreator = new BjarneStroustrup();
var javaCreator = new JamesGosling();
var pythonCreator = new GuidoVanRossum();
var defaultCreator = new JonathanBlow();
// Setup Chain of Responsibility
cppCreator.SetSuccessor(javaCreator);
javaCreator.SetSuccessor(pythonCreator);
pythonCreator.SetSuccessor(defaultCreator);
// Process requests to sign the books
string[] requests = ["C++", "Ada", "Java", "Python", "C#"];
foreach (var request in requests)
{
cppCreator.SignBook(request);
}
}
}
// outputs:
// BjarneStroustrup signs the C++ book.
// Ada author not present, but JonathanBlow signs!
// JamesGosling signs the Java book.
// GuidoVanRossum signs the Python book.
// C# author not present, but JonathanBlow signs!
Summary
For more information, please see here.