Here’s the UML diagram:So we must create an abstract handler.
The abstract handler SetNextmethod will be responsible for setting the next handler on the chain.
Each rule will extend this handler and override the Handle method and decide if it should handle the request, or pass it to the next handler.
The abstract class Handler method calls the next handler Handle.
We don’t have a request object yet.
Let’s create it.
It’s the object that is going to be passed through the chain.
It must contain all the data required by the handlers.
In our case, it’s just a Data Transfer Object (DTO) that stores those GetClass local variables it created at lines 9 to 13:The object that’s going to be handled by each ruleNow our GetClass method looks like this:Before moving on, we should get rid of these repeating hardcoded strings, so our code responds easier to future changes by storing the string values in a single place.
Let’s create a label class with some static factory methods :Future proofing by keeping hard coded strings in a single placeNow we know that our chain of responsibility should handle a Request and return a Label .
Let’s create our abstract handler based on that UML diagram:The Runmethod is going to be overridden by each rule, which will decide if they should either yield the Labelto the client or delegate it to the next rule.
The SetNext method will not be overridden, but will be used in client code to set the chain in the right rule precedence order.
It returns the next rule so we can set the chain using a fluent API.
Now let’s create our first rule.
If you remember from the GetClass method, this is the first one:We need to create a class that inherits from the abstract RuleHandler , overrides itsRun method and decides if it should return the proper Label or pass the request to the next handler.
Based on the stuff the conditional statement is checking, I’m going to call the rule CriteriaHasEvaluation :Notice that I’ve used the Label class instead of the original code’s hard coded strings.
So, given the request, the rule checks if it should yield the result or delegate it to the next handler.
This is the drill, and the next rules are going to follow the same structure.
But let’s make another one just to get used to it.
The next rule in the chain is:I’m going to call it EffectivenessAssessmentPending :I’m going to skip the creation of the IsNew and CriteriaEvaluated rules, so we can go straight to our client code.
Now we have a bunch of rules.
We need to set the precedence of them, run the chain, and return the result.
We’re going to use the SetNext method to assemble the chain, respecting the rules precedence.
Then we are going to Run the chain, and return the result.
The client API surface is now so simple, that I’m going to show the final GetClass method after the refactoring:So we set the chain using the abstract RuleHandler’s SetNext method.
It sets the next rule to the current rule, so when we call Run we run the chain in the set order.
It returns next rule, so we can set the chain… chaining methods!It’s way more clean than the old code right?.Take a look at it again:We removed the hardcoded strings, moved the conditions to self documenting rules, and left the code cleanerGenericsThe client code is cleaner, but we created some boilerplate code, like the abstract RuleHandler .
This abstract class can’t be reused, since it returns and handles only the types we needed for this particular use case(Request and Label ).
We can reduce the amount of boilerplate code used to create a Chain of Responsibility by using C# Generics.
Making the rule handler genericNow we can reuse this generic abstract rule handler in other chains.
We just need to specify the types when creating a rule:Extends RuleHandler<Request,Label>Wrapping UpKinda over-the-top?.Yeah, I just wanted to show a real world example of a design pattern use, because sometimes everything seems so abstract and academic.
But there are plenty of other interesting use cases, like tax processing, ordering pipelines, and you’re probably using something similar like an API middleware.
I have a login pipeline simulation in an open source repository here.
The Chain of Responsibility has another cool feature: It’s composable.
You could reorder the chain’s rules precedence based on run time conditions.
Pretty flexible right?If you use an object oriented language like C#, PHP or Java it’s nice to play with some Design Patterns, because sometimes you can come up with an elegant solution to a hard solving problem, or at least have a lot of fun!.