In this third post on the SOLID principles we will look at the Open/Closed Principle (OCP).
Bertrand Mayer coined the Open/Closed Principle in 1988, and it states that ”A software entity (class, module, function) should be open for extension but closed for modification”.
A software entity complies to OCP if its behavior can be extended, and extending the behavior does not result in changes to it’s existing code.
The most obvious code smell that appears for entities that does not follow OCP is Rigidity i.e. that the software is hard to change. A change to one entity results in a cascade of changes in other entities.
This also leads to Fragility, since being forced to make changes in many different entities makes it easy to break something. Your code base may also show signs of Immobility.
How to apply
NOTE: As I wrote in the previous posts, applying patterns and abstractions should be done only when smells are appearing. I recommend that you use the ”Fool me once” approach. That is, start with writing the code as if it is never going to change, then the first time you do need to change it, you apply the appropriate patterns and abstractions.
Your goal when applying OCP should be to make it easy to introduce changes by adding new code (open for extension), but not by modifying existing code that already works (closed for modification).
But how can that be done? The two statements sounds like contradictions. The answer, abstraction. There are two patterns that can be used to achieve this goal, and they both have the approach of splitting up concrete classes into abstract and concrete parts. The patters are The Template Pattern and The Strategy Pattern.
The Template Pattern
The Template Pattern uses inheritance to separate a generic algorithm from a detailed context. You apply it by splitting your current concrete class into two:
- An abstract base class containing the generic code/algorithm, this is your template
- The concrete context, that inherits from the abstract base class and implements the abstract methods
Applying this pattern opens up your code for extension by making it possible to add new behavior by adding new concrete classes that inherits from the abstract base class. You can do this without modifying the existing base class or any existing concrete classes.
The Strategy Pattern
The Strategy Pattern is also used to separate a generic algorithm from a detailed context. Unlike the Template Pattern it does so by delegation and not inheritance.
To apply the Strategy Pattern you split up your concrete implementation into three parts:
- A concrete class containing the generic algorithm
- An interface that abstracts the detailed context
- A concrete strategy class that implements the interface
To use these classes you first instantiate the strategy class (3) and hand it to the class containing the generic algorithm (1), preferably by constructor injection.
This opens up your code for extension by allowing you to add new strategies (3) without having to modify any of the existing code. The generic algorithm can also be re-used by given different strategies.
The Open/Closed Principle is the second of the five principles of SOLID. It can be a bit hard to understand what it means that something is ”open for extension but closed for modification”, hopefully this post has made it a bit clearer. I advise you to read up a bit more on both the template, and the strategy, pattern.
Next, we will dive into the third principle of SOLID, The Liskov Substitution Principle.