Open-Closed Principle (OCP) is the second amongst the five design principles stipulated by SOLID.
Objects or entities should be open for extension, but closed for modification.
What it means?
Open for extension means that we should be able to add new features or components to the application without breaking existing code.
Closed for modification means that we should not introduce breaking changes to existing functionality, because that would force you to refactor a lot of existing code
Let’s understand better with an example
Consider, that you work for a company which has a portal to sell its products. These products might have various attributes to classify them like colour, size etc. One of the features of this portal is to allow customers to filter products based on these attributes.
We will try and design a simple console application that demonstrates how this can be done. After this we’ll first see how Open-Closed principle might get violated and ways to overcome this.
We’ll create the following Enums to showcase the product attributes.
Let’s now create a
Product class to denotes the Product in our example.
We’ll now come up with a
ProductFilter class which will help use assign filters based on the Product attributes.
Now that we have the core stuff in place, lets see how we can put the above to use in our
Main method of our Console application.
As can be seen we have created a few products and are trying to simulate filtering them by colour (in this case - Blue). Output is…
Similarly, we could also filter these products based on size using the other filter method
FilterBySize in our
All good so far!
Violating the Open-Closed Principle
Now, supposing your boss comes up to you and states that a new filter be available whereby customer can filter by both colour and size. This is not possible with the existing implementation. To make this possible we’ll have to update the
ProductFilter class with a new method
FilterByColourAndSize as …
We can then get this working in the
Main method using …
If you were observant enough, you must have noticed that we violated the Open-Closed Principle while updating the
ProductFilter class above.
Refactoring and aligning to the Open-Closed Principle
To set this right, we can use and Enterprise pattern called the Specification pattern. We’ll define a specification interface with a single method as …
IsSatisfied method will be used to actually validate the conditions applicable on the objects properties. We’ll also define a filter interface, to apply these specifications to the object in consideration, in this case -
Now, using both these interfaces let’s attempt to rewrite our earlier approach to apply the colour filter.
We’ll also re-write the
ProductFilter class and rename it as
ProductFilterImprovised class with the refactored design as …
We can now apply the colour filter on Products from the
Main method as ..
This will give us the same output as earlier, however, with a much more extensible codebase, whereby, we no longer have to violate the Open-Closed Principle to add additional filters. To prove this, lets try to add a filter to both the colour and size attributes as earlier, but, with the new design. Let’s first create a specification for the size on the same lines as colour.
Now, since we need to use both colour and size to apply the filter, we’ll create a
AndSpecification class which would help combine both size and colour as …
We can now use this in the
Main method as ….
Run the application and you’ll get the desired output…
The best part was that we did not have to touch any of the original classes for Specification nor the Filter class to achieve this, thus aligning to the Open-Closed Principle. You may download the code from my github repository here
Hope this was useful!!