This principle states the most fundamental rule to make your software flexible. All other OOP principles, methodologies and conventions revolve around this principle. Open-Closed Principle (OCP) states:
Software entities should be open for extension, but closed for modification.
At first glance, it seems self-contradicting. Lets define the following terms to get more clarity:
- Software entities are functions, methods, modules, classes in the design.
- Open for extension means that its behaviour can be extended or changed depending
- Closed for modification restricts changing source code of the module for extending or changing its behaviour.
Open of extension attribute addresses the real need of softwares to change with time as business requirements change. This change though, should not cause the source code to change. Why? We can realise its advantage as soon as we consider a code consisting of
if-then-else logic. Lets consider the logging example, which we had considered in Dependency Inversion Principle.
logger module: form_message write_message_to_file
The business now requires that the logging should be done to the database, rather than a file. So here goes the new logic:
logger module: form_message if target is file write_message_to_file else write_message_to_db endif
It works, definitely works. The problem is that in future if the requirements change again to log in a, say XML file, the source code will have to be changed again. Any change in the source code might not only insert bugs but also increase the amount of testing to be done to make sure it works fine and does not break anything. Especially if this change has to be done in the core part of a design, entire testing suites might have to be executed. Open-Closed Principle advocates making the software flexible by incremental code, add code rather than change code.
The answer, again, is abstraction. Abstraction enables you to have an interface which separates the intent from the implementation, or the what from the how. This opens possibilities for multiple hows for the same what.
In languages with syntactical support for OOP, like C++, Java, Python, Ruby, and many more; this can be done using interface or implementation inheritance. Multiple forms of the same object is possible through polymorphism.
The above algorithm can be converted to:
logger module: form_message write_message(target) target: write file derived from target: write: write_to_file db derived from target: write: write_to_db
If logging has to be moved to XML file, then we can simply add another target derivative:
xmlfile derived from target: write: write_to_xmlfile
The logger modules does not have to change to add a new destination for logging. This is open for extension but closed for modification.
A module cannot be completely closed against all changes. If some of its core logic has to be changed, then the module code will have to changed. However, the module should be closed for extensions, it should provide alternative ways of extending itself.
This rule has branched into lot of corollaries or design conventions. The two most popular ones are:
- All member variables should be private. This is prompted from the fact that a closure cannot be enforced if the member variable can be accessed directly, leaving possibility for violation of OCP. Every member variable should be private and its access should be controlled by appropriate methods.
- No global variables. Global variables are dangerous because they are accessible to everyone, and available for writing. Someone can inadverently write into a global variable which can crash the whole system.
We can define some coding practices like avoid multiple
Following links present code examples for this design principle:
Back to Design Principles.
Copyright Abhijit Nadgouda.