Design patterns are not difficult to learn, but mastering them takes time. Still, it's absolutely worth it! Knowledge on design patterns is necessary when you want to grow as a professional developer and build better software.
Design patterns can be divided into three groups: creational, structural, and behavioral. Chain of Responsibility belongs to the behavioral one – which is a group of design patterns that help with better interaction between objects in your code.
TIP: There is a classic book discussing this topic and describing 23 design patterns - "Design Patterns: Elements of Reusable Object-Oriented Software". You should definitely read it if you are not familiar with the matter.
Let's learn more about the Chain of Responsibility with some practical examples
The Chain of Responsibility pattern is used to replace statements like if ... else if ... else with more object-oriented form. Let's consider calculating a tax amount that is different for each country. The first version of the method handling this calculation could look like this:
It does not look that complicated, but the code can easily become longer and more laborious with time. In the next version, the tax may be calculated differently for various types of products, we may also have more countries that we want to calculate tax for. Moreover, at some point in the future, the law may be changed and that will result in different formulas based on date. This is a perfect example to use the Chain of Responsibility pattern. Take a look at the tax calculation problem implemented with this pattern:
In this version, we don't have five if statements in one method and each type of tax is calculated by its own class. That way our code follows the Single Responsibility Principle, which is one of the most important rules in clean coding. To use these new classes, we have to create an instance of each class and set value for
Next property. See an example here:
If you had trouble with understanding how it works, I recommend you set a breakpoint and use a debugger to check which instructions are executed.
Here is another example – an algorithm that recommends you the next video to watch based on your age. This is a really simple example because it always returns the same video, but it fits nicely into the Chain of Responsibility pattern.
And the third example – it's the code for an ATM that checks if it can dispense cash using a finite number of notes inside the machine. In this case, we will have just one implementation of an abstract class, but we will define a chain consisting of three links using that one class.
Here is how we can use it:
By now, I think you have already grasped the concept of the Chain of Responsibility pattern. Here is a class diagram and a sequence diagram that presents it:
Chain of Responsibility vs other patterns
Chain of Responsibility vs Decorator
The Chain of Responsibility design pattern may appear similar to the Decorator design pattern but the key difference is that the former can be stopped at any part of the processing, and the latter executes all steps (assuming that passed data was valid and it didn't throw an exception). Decorator adds something with each step.
Chain of Responsibility vs Strategy
With the Chain of Responsibility pattern, each object is responsible for executing an action on the next object, whereas, with the Strategy pattern, the client of that code has to decide which implementation should be used.
I strongly recommend you to read the book mentioned in the intro: Design Patterns: Elements of Reusable Object-Oriented Software. You can also watch a series of courses on Pluralsight in the design patterns path. There is also a fun experiment that you can check out on GitHub: FizzBuzz Enterprise Edition is a no-nonsense implementation of FizzBuzz made by serious businessmen for serious business purposes.