A powerful design pattern geared towards handling an event is the Chain of Responsbility design pattern. This pattern helps decouple related algorithms in C# ASP .NET and can help your software become more scalable and easier to control.
In general, the Chain of Responsbility design pattern allows you to link together a serious of classes or algorithms in a hierarchy and pass a command (preferably a Command pattern object) into the mix to get handled by one of the members of the chain. One of the benefits of this design pattern is, not having to know the details of the members in the chain. You simply pass the command into the chain. Each class will attempt to handle the request until all classes have been used. While it is possible for a request to go unhandled, this is a powerful design pattern when used appropriately.
As with all first programming examples, the Hello World example is a good introduction to an implementation of the Chain of Responsbility design pattern in C# ASP .NET. However, first let’s take a look at the standard way of handling a request without using the Chain of Responsbility.
As you can see in the above example, to handle MyRequest we pass it to each Handler function that we’ve defined. There are several drawbacks to this simplistic method, especially in an object oriented language such as C# .NET. Foremost, there are many conditionals which create confusion when reading the code. This can especially become a problem when performing team development and debugging. More importantly, detailed knowledge of the handler implementations are required since they are listed within the conditional.
The above example can be enhanced with a Chain of Responsbility as shown in the following example:
In this example, we have implemented a Chain of Responsiblity design pattern with C# ASP .NET which handles the request. After setting up the initial chain, we only need to pass MyRequest to the first handler (which is of the generic type ‘Handler’). Knowledge of which handler gets called next is preserved within the chain itself.
By following the above example for implementing a Chain of Responsibility, we can apply it to create a simple Hello World example as shown in the following code.
Notice that there will be three handlers, an English, a French, and a Spanish handler. The command request will consist of two parts: a culture indicator and the text to display. For this example, a simple string was used in place of a Command design pattern. The culture indicator is just the first 5 characters of the string. Each handler will look at the culture indicator to determine whether it should handle the printing of the text to the console. If the handler can not handle the command, it passes it to the next handler in the chain, until all handlers have been ran.
To get to the meat of the Chain of Responsibility, we need to define the generic class that our Handlers will inherit from.
We have chosen to use an abstract class in place of an interface for implementing the generic Handler class. This is due to the fact that our Handler class will implement the body of one of the functions as well as define a protected member. Also note that abstract classes, in general, perform faster than interfaces due to less memory lookups.
This Handler definition is fairly simple. The most important part is the inclusion of a protected successor object, of type Handler. This allows a form of a linked list to implement our chain. We also implement a SetSuccessor function to assign the successor. The HandleRequest function will be required by all handlers to initiate a possible action.
Now that we have the abstract class Handler defined, we can now create some concrete handlers to create a complete Chain of Responsibility in C# ASP .NET. Our first handler will handle English strings by looking in the command for the culture identifier “en-us”.
The EnglishHandler Class
Notice our handler inherits from our abstract base class Handler and defines the required HandleRequest function. The body of the function simply checks the first 5 characters of the command string to see if it contains “en-us”. If it does, the payload portion of the string is printed to the console, the command is deemed handled, and execution of the chain stops. If “en-us” is not found, the handler passes the command to the next handler in line. Of course, if no other handler exists, the command remains unhandled. Unhandled commands could be managed as well by including additional detection code.
We can create two more handlers to make the example more interesting, as follows.
Note, the FrenchHandler differs only by the culture identifier that it is looking for and by the action performed on the payload. Just like the EnglishHandler, the FrenchHandler passes the command to the next in line if it fails to handle it. The FrenchHandler doesn’t need to know the details about its successor, only that it’s of type Handler.
Now that we have created 3 handlers, our original main code can be executed. We begin by defining three handlers of each concrete handler type. We then setup the chain in our desired order. Typically, order doesn’t matter since each handler will have a chance at the request. However, speed issues may arise where order becomes critical. Finally, we send a request to the first member of the chain and the Chain of Responsibility handles the rest. Notice in the main program above that we execute 3 requests. Each one will be handled by one of the concrete handlers.
Since the concrete handlers inherit from the generic Handler class, they have no need to know the concrete details about their successors. This allows us to implement new concrete handlers (for other languages, for example), and insert them anywhere in the chain.
C# delegates can be a form of a chain of responsiblity in themselves. By creating a delegate and assigning it multiple functions for execution, a multicast delgate is created and may be used in a similar fashion to a chain of responsibility design pattern. When the delegate is invoked, each function added to the multicast delgate’s chain is executed. Where a typical chain of responsiblity pattern will only continue executing handlers until one succeeds, the multicast delegate will always execute each handler. For example:
The Chain of Responsibility design pattern helps you create more scalable and reusable code in C# ASP .NET. By defining concrete handlers inidividually, you help decouple algorithms into separate and reusable classes. A request can be passed to the first handler and executed by any member of the chain, regardless of whether those handlers, or their concrete types, are known beforehand. C# .NET provides mutlicast delegates which may be considered a form of a chain of responsibility. Developers can take advantage of multicast delegates to perform event and callback functionality or design their own chain of responsiblity pattern with C# ASP .NET.
This article was written by Kory Becker, software developer and architect, skilled in a range of technologies, including web application development, machine learning, artificial intelligence, and data science.