Usage examples: The Chain of Responsibility is pretty common in C#. It’s mostly relevant when your code operates with chains of objects, such as filters, event chains, etc.
Identification: The pattern is recognizable by behavioral methods of one group of objects that indirectly call the same methods in other objects, while all the objects follow the common interface.
Conceptual Example
This example illustrates the structure of the Chain of Responsibility design pattern. It focuses on answering these questions:
What classes does it consist of?
What roles do these classes play?
In what way the elements of the pattern are related?
Program.cs: Conceptual example
usingSystem;usingSystem.Collections.Generic;namespaceRefactoringGuru.DesignPatterns.ChainOfResponsibility.Conceptual{ // The Handler interface declares a method for building the chain of // handlers. It also declares a method for executing a request.publicinterfaceIHandler {IHandlerSetNext(IHandler handler);objectHandle(object request); } // The default chaining behavior can be implemented inside a base handler // class.abstractclassAbstractHandler:IHandler {privateIHandler _nextHandler;publicIHandlerSetNext(IHandler handler) {this._nextHandler= handler; // Returning a handler from here will let us link handlers in a // convenient way like this: // monkey.SetNext(squirrel).SetNext(dog);return handler; }publicvirtualobjectHandle(object request) {if (this._nextHandler!=null) {returnthis._nextHandler.Handle(request); }else {returnnull; } } }classMonkeyHandler:AbstractHandler {publicoverrideobjectHandle(object request) {if ((request asstring) =="Banana") {return$"Monkey: I'll eat the {request.ToString()}.\n"; }else {return base.Handle(request); } } }classSquirrelHandler:AbstractHandler {publicoverrideobjectHandle(object request) {if (request.ToString() =="Nut") {return$"Squirrel: I'll eat the {request.ToString()}.\n"; }else {return base.Handle(request); } } }classDogHandler:AbstractHandler {publicoverrideobjectHandle(object request) {if (request.ToString() =="MeatBall") {return$"Dog: I'll eat the {request.ToString()}.\n"; }else {return base.Handle(request); } } }classClient { // The client code is usually suited to work with a single handler. In // most cases, it is not even aware that the handler is part of a chain.publicstaticvoidClientCode(AbstractHandler handler) {foreach (var food innewList<string> { "Nut","Banana","Cup of coffee" }) {Console.WriteLine($"Client: Who wants a {food}?");var result =handler.Handle(food);if (result !=null) {Console.Write($" {result}"); }else {Console.WriteLine($" {food} was left untouched."); } } } }classProgram {staticvoidMain(string[] args) { // The other part of the client code constructs the actual chain.var monkey =newMonkeyHandler();var squirrel =newSquirrelHandler();var dog =newDogHandler();monkey.SetNext(squirrel).SetNext(dog); // The client should be able to send a request to any handler, not // just the first one in the chain.Console.WriteLine("Chain: Monkey > Squirrel > Dog\n");Client.ClientCode(monkey);Console.WriteLine();Console.WriteLine("Subchain: Squirrel > Dog\n");Client.ClientCode(squirrel); } }}
Output.txt: Execution result
Chain: Monkey > Squirrel > Dog
Client: Who wants a Nut?
Squirrel: I'll eat the Nut.
Client: Who wants a Banana?
Monkey: I'll eat the Banana.
Client: Who wants a Cup of coffee?
Cup of coffee was left untouched.
Subchain: Squirrel > Dog
Client: Who wants a Nut?
Squirrel: I'll eat the Nut.
Client: Who wants a Banana?
Banana was left untouched.
Client: Who wants a Cup of coffee?
Cup of coffee was left untouched.