The OCP is simple to write, but difficult to explain. For this reason, I'll write the following definition first and describe it later:
New features can be added to an existing module by extension and not by modification.
It sounds simple, doesn't it? In order to understand this concept from a practical viewpoint, it is necessary to revisit our last example. Let's check that we are accomplishing this principle by answering the following questions:
- What do we need in order to support a new notification channel?
We need to write a new class (module), and this should implement an existing interface. Note how the open-closed principle makes sense with the provided answer. To support a new notification channel in our application, we need to create a new class, but we don't need to modify the existing code. According to the previous refactoring that we made, if we needed to support this requirement, we had to adjust the existing service to send notifications.
A few questions to validate how well this principle is achieved are as follows:
-
- Do I add a new IF statement to my code?
No. If you're looking to add a new feature, you will write a new class instead of modifying an existing one. This is because you are adding and not changing features.
-
- How much code do I modify in order to support a new feature?
Hopefully, just a little bit. In a perfect world, you won't need to modify anything, but sometimes a few sections should be changed to support new features in the real world. The rule here is that if you are adding a new feature, your original design should be able to support this requirement with minimal changes. If this is not true, refactoring or changing your initial design is recommended.
-
- How big should my source code files be?
Big source code files are a bad idea, and there is no reason for them to be large. If your source code file has hundreds and hundreds of lines, revisit your functions and think about moving code to a new file in order to make the source code files smaller and easy to understand.
-
- Should I use abstractions within my code?
This is a tricky one. If you only have one concrete implementation for something, you won't need to have an abstract class or interface. Writing code and inventing new possible scenarios is not desirable at all, but if you have at least two concrete implementations that are related to each other, you have to think about writing an abstraction for them. For example, if we only need to send email notifications, there would be no reason to write an interface for this. However, since we are sending notifications via two different channels, we certainly need an abstraction to deal with them.