Open Closed Principle in SOLID

Read below before reading blow one

The Open Closed Principle is one of the SOLID principles defined by Robert C. Martin. The principle says “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”.

So in simple words it says that an implementation (of a class or function), once created, should be closed for further modification, in other words one should not modify an implementation (of a class or function) of logic and/or functionality. One can do refactoring or resolve errors of implementation (of a class or function) but the implementation (of a class or function) is open for extension, in other words one can extend the implementation (of a class or function) of logic and/or functionality.

Real Life Example of Open Closed Principle

An electric adapter is a good example of this principle.

As you can see in the image:
  1. An adapter in the wall is always closed for modification, in other words we cannot change it once it is fitted or extended if we want more.

  2. But an adapter always provides a method of extension, so we can plug in an extension board of an adapter for more adaptation.

  3. So you plug in an extension board and extend an existing electric adapter fitted in wall.

Example of not following the principle in Application Development

A bank offers various types of the savings accounts (Salary Saving, Regular Saving and so on) that meet the needs of many types of customers. A bank has differing sets of rules and each savings account type has a different set of rules to calculate interest.

To calculate the interest of an account, developers have developed the following class with methods to calculate interest.

  1. Public class SavingAccount  
  2. {  
  3.     //Other method and property and code  
  4.     Public decimal CalculateInterest(AccountType accountType)  
  5.     {  
  6.         If(AccountType==”Regular”)  
  7.         {  
  8.             //Calculate interest for regular saving account based on rules and   
  9.             // regulation of bank  
  10.             Interest = balance * 0.4;  
  11.             If(balance < 1000) interest -= balance * 0.2;  
  12.             If(balance < 50000) interest += amount * 0.4;  
  13.         }  
  14.         else if(AccountType==”Salary”)  
  15.         {  
  16.             //Calculate interest for saving account based on rules and regulation of   
  17.             //bank  
  18.             Interest = balance * 0.5;  
  19.         }  
  20.     }  
  21. }  
So in the preceding code the SavingAccount class's CalculateInterest method does a calcualtion based on the account type like Salary and Regular.

So the implementation is not following the Open Closed principle because if tomorrow the bank introduces a new SavingAccount type then there is a requirement to modify this method for adding a new case for the new account type. An example is if the bank introduces a “Child Savings Account type” requiring a new condition for calculating the interest for this account type. That means that the method is always open for modification.

One more thing to note here is that the method is also not following the Single Responsibility Principle. Since here the method is doing more than one thing, like calculating interest for more than one type.

How to Implement the Open Closed Principle

Inheritance is only one way to implement the Open Closed Principle. Because inheritance is only an Object Oriented Design (OOD) basic pillar that allows extension of functionality of an existing class.

To implement the Open Closed Principle one can use interface, an abstract class, abstract methods and virtual methods than inherit them when you want to extend functionality.

So the preceding problem of a Savings Account can be resolved as in the following.

  1. Interface ISavingAccount  
  2. {  
  3.    //Other method and property and code  
  4.    decimal CalculateInterest();  
  5. }  
  6. Public Class RegularSavingAccount : ISavingAccount  
  7. {  
  8.   //Other method and property and code related to Regular Saving account  
  9.   Public decimal CalculateInterest()  
  10.   {  
  11.     //Calculate interest for regular saving account based on rules and   
  12.     // regulation of bank  
  13.     Interest = balance * 0.4;  
  14.     If(balance < 1000) interest -= balance * 0.2;  
  15.     If(balance < 50000) interest += amount * 0.4;  
  16.   }  
  17. }  
  19. Public Class SalarySavingAccount : ISavingAccount  
  20. {  
  21.   //Other method and property and code related to Salary Saving account`  
  22.   Public decimal CalculateInterest()  
  23.   {  
  24.     //Calculate interest for saving account based on rules and regulation of   
  25.     //bank  
  26.     Interest = balance * 0.5;  
  27.   }  
  28. }  
In the preceding code two new classes are created, RgularSavingAccount and SalarySavingAccount, that is inherited from IsavingAccount.

So if there is a new account added by the bank than there is no need to modify the logic of exiting classes, just extend the functionality by inheriting an interface.

Finally the preceding example implements the Open Closed Principle since there is no need to modify the existing implemented logic and it allows extension for adding new logic.

And the preceding example also implements the Single Responsibility Principle since each class or function is doing only one task.

Note : An interface is created here just as an example. There could be an abstract class of SavingAccount that is implemented by a new savings account type.

Read: Decorate design principle that also helps to understand.

Disadvantage of not following Open Closed Principle  
  1. Since a class or function always allows the addition of new logic, whenever new logic is added it is always necessary to test for full functionality. That requires the addition of a new test case for the added functionality and might also require the modification of an existing test case that fails because of added functionality.

  2. It also breaks the Single Responsibility Principle since a class or function might end up doing multiple tasks.

  3. Class or function maintenance becomes difficult since a class or function can become thousands of lines of code that is difficult to understand.

How to Identify not following Open Closed Principle

  • A class or function is always open for modification, in other words always allows adding more logic to it. Like in the preceding example.

Final Thoughts

Both Single Responsibility and Open Closed Principle highly depend on each other. If one breaks then others break. One should always think when creating the design of the system.