The Template Method is a powerful and easy-to-implement design pattern allowing subclasses to reuse the algorithm’s skeleton while implementing (abstract) or overriding (virtual) subparts. It allows implementation-specific classes to extend the core algorithm. It can reduce the duplication of logic and improve maintainability while not cutting out any flexibility in the process. There are many examples in the .NET BCL, and we leverage this pattern at the end of the chapter based on a real-world scenario.Now, let’s see how the Template Method pattern can help us follow the SOLID principles:
- S: The Template Method pushes algorithm-specific portions of the code to subclasses while keeping the core algorithm in the base class. Doing that helps follow the single responsibility principle (SRP) by distributing responsibilities.
- O: By opening extension hooks, it opens the template for extensions (allowing subclasses to extend it) and closes it from modifications (no need to modify the base class since the subclasses can extend it).
- L: As long as the subclasses are the implementations and do not alter the base class per se, following the Liskov substitution principle (LSP) should not be a problem. However, this principle is tricky, so it is possible to break it; by throwing a new type of exception or altering the state of a more complex base class in a way that changes its behavior, for example.
- I: As long as the base class implements the smallest cohesive surface possible, using the Template Method pattern should not negatively impact the program. On top of this, having a smaller interface surface in classes diminishes the chances of breaking the LSP.
- D: The Template Method pattern is based on an abstraction, so as long as consumers depend on that abstraction, it should help to get in line with the dependency inversion principle (DIP).
Next, we move to the Chain of Responsibility design pattern before mixing the Template Method and the Chain of Responsibility pattern to improve our code.
Implementing the Chain of Responsibility pattern
The Chain of Responsibility is a GoF behavioral pattern that chains classes to handle complex scenarios efficiently, with limited effort. Once again, the goal is to take a complex problem and break it into multiple smaller units.
Goal
The Chain of Responsibility pattern aims to chain multiple handlers that each solve a limited number of problems. If a handler cannot solve the specific problem, it passes the resolution to the chain’s next handler.
We often create a default handler that executes logic at the end of the chain as the terminal handler. Such a handler can throw an exception (for example, OperationNotHandledException) to cascade the issue up the call stack to a consumer who knows how to handle and react to it. Another strategy is creating a terminal handler that does the opposite and ensures nothing happens.