SOLID Principles In C# - Open Closed Principle

Introduction

 
C# is an object-oriented programming language. These days whenever you talk about object-oriented programming you hear the acronym, SOLID. These are five design principles introduced by Michael Feathers to make our object-oriented applications easy to understand, maintain and expand as future requirements change. Today, we will look at the second principle with an example. I covered the first principle in my previous article.
 

The SOLID principles

 
There are five principles to follow to ensure our application meets the SOLID requirements. These are as below,
  1. Single Responsibility Principle (SRP)
  2. Open Closed Principle (OCP)
  3. Liskov Substitution Principle (LSP)
  4. Interface Segregation Principle (ISP)
  5. Dependency Inversion Principle (DIP)

The Open Closed Principle (OCP)

 
The Open Closed Principle (OCP) states that a class must be open to be extended but closed to changes. This will ensure the class and ultimately the whole application is very robust and easy to maintain and expand, if required. Let us look at this with an example:
 
Let us create a new .NET Core 3.1 console application in Visual Studio 2019 Community Edition as below,
 
SOLID Principles In C# - Open Closed Principle
 
SOLID Principles In C# - Open Closed Principle
 
Inside this project, I have created a new class file “OpenClosedPrinciple”. In this file, I create the following “BankAccount” class.
  1. // Not following the Open Closed Principle  
  2.   
  3.     public enum AccountType  
  4.     {  
  5.         SavingsAccount = 1,  
  6.         LongTermSavingsAccount = 2  
  7.     }  
  8.   
  9.     public class BankAcount  
  10.     {  
  11.         public string AccountNumber { getset; }  
  12.   
  13.         public string AccountTitle { getset; }  
  14.   
  15.         public AccountType BankAccountType { getset; }  
  16.   
  17.         public float CalculateInterest(float accountBalance)  
  18.         {  
  19.             // Add an IF statement for each type of new bank account -- violates OCP  
  20.             if (BankAccountType == AccountType.SavingsAccount)  
  21.                 return accountBalance * 0.02F;  
  22.             if (BankAccountType == AccountType.LongTermSavingsAccount)  
  23.                 return accountBalance * 0.04F;  
  24.             else  
  25.                 return accountBalance;  
  26.         }  
  27.     }  
This class does not follow the “Open Closed Principle” as whenever we need to add a new type of bank account, we would need to add another if statement to the CalculateInterest function. Hence, we have opened the class to changes.
 
We can fix this as below,
  1. // Following the Open Closed Principle  
  2.     public interface IBankAccount  
  3.     {  
  4.        float CalculateInterest(float accountBalance);  
  5.     }  
  6.   
  7.     public abstract class BankAccountBase  
  8.     {  
  9.         public string AccountNumber { getset; }  
  10.         public string AccountTitle { getset; }  
  11.         public AccountType BankAccountType { getset; }  
  12.     }  
  13.   
  14.     public class SavingsAccount : BankAccountBase, IBankAccount  
  15.     {  
  16.         public float CalculateInterest(float accountBalance)  
  17.         {  
  18.             return accountBalance * 0.02F;  
  19.         }  
  20.     }  
  21.   
  22.     public class LongTermSavingsAccount : BankAccountBase, IBankAccount  
  23.     {  
  24.         public float CalculateInterest(float accountBalance)  
  25.         {  
  26.             return accountBalance * 0.04F;  
  27.         }  
  28.     }  
In the above code, we see that we have created an interface for the CalculateInterest function and an abstract class with the common elements of all bank accounts. Next, we create a separate class for each bank account type by inheriting from the base class and implementing the interface. In this way, if we need to add a new bank account type, we will simply create a new class for it and inherit from the account base class and implement the IBankAccount interface and apply the logic for calculating the interest for this particular class here. This is extending and not changing the existing classes and interfaces.
 
If we run both the implementations above, we get the same results, but one has applied the OCP correctly and the other has not.
  1. var bankAccount = new BankAcount()  
  2.             {  
  3.                 AccountNumber = "100AAA",  
  4.                 AccountTitle = "Mr. John Doe",  
  5.                 BankAccountType = AccountType.SavingsAccount  
  6.             };  
  7.   
  8.             Console.WriteLine($"The Interest amount for {bankAccount.AccountTitle} is ${bankAccount.CalculateInterest(1000)}");  
  9.   
  10.             //Calling class following Open Closed Principle  
  11.   
  12.             var savingsBankAccount = new SavingsAccount()  
  13.             {  
  14.                 AccountNumber = "100AAA",  
  15.                 AccountTitle = "Mr. John Doe",  
  16.                 BankAccountType = AccountType.SavingsAccount  
  17.             };  
  18. Console.WriteLine($"The Interest amount for {savingsBankAccount.AccountTitle} is ${savingsBankAccount.CalculateInterest(1000)}");  
SOLID Principles In C# - Open Closed Principle
 

Summary

 
In this article, we have looked at implementing the Open Closed Principle (OCP) in a practical example. I would recommend you look though your existing classes and identify places where you have violated this principle and then think of ways to fix it. This will help to get you thinking in terms of applying this principle and help you to apply it to your code in the future as well. In my next article, we will look at the Liskov Substitution Principle.


Similar Articles