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,
- Single Responsibility Principle (SRP)
- Open Closed Principle (OCP)
- Liskov Substitution Principle (LSP)
- Interface Segregation Principle (ISP)
- 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,
Inside this project, I have created a new class file “OpenClosedPrinciple”. In this file, I create the following “BankAccount” class.
-
-
- public enum AccountType
- {
- SavingsAccount = 1,
- LongTermSavingsAccount = 2
- }
-
- public class BankAcount
- {
- public string AccountNumber { get; set; }
-
- public string AccountTitle { get; set; }
-
- public AccountType BankAccountType { get; set; }
-
- public float CalculateInterest(float accountBalance)
- {
-
- if (BankAccountType == AccountType.SavingsAccount)
- return accountBalance * 0.02F;
- if (BankAccountType == AccountType.LongTermSavingsAccount)
- return accountBalance * 0.04F;
- else
- return accountBalance;
- }
- }
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,
-
- public interface IBankAccount
- {
- float CalculateInterest(float accountBalance);
- }
-
- public abstract class BankAccountBase
- {
- public string AccountNumber { get; set; }
- public string AccountTitle { get; set; }
- public AccountType BankAccountType { get; set; }
- }
-
- public class SavingsAccount : BankAccountBase, IBankAccount
- {
- public float CalculateInterest(float accountBalance)
- {
- return accountBalance * 0.02F;
- }
- }
-
- public class LongTermSavingsAccount : BankAccountBase, IBankAccount
- {
- public float CalculateInterest(float accountBalance)
- {
- return accountBalance * 0.04F;
- }
- }
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.
- var bankAccount = new BankAcount()
- {
- AccountNumber = "100AAA",
- AccountTitle = "Mr. John Doe",
- BankAccountType = AccountType.SavingsAccount
- };
-
- Console.WriteLine($"The Interest amount for {bankAccount.AccountTitle} is ${bankAccount.CalculateInterest(1000)}");
-
-
-
- var savingsBankAccount = new SavingsAccount()
- {
- AccountNumber = "100AAA",
- AccountTitle = "Mr. John Doe",
- BankAccountType = AccountType.SavingsAccount
- };
- Console.WriteLine($"The Interest amount for {savingsBankAccount.AccountTitle} is ${savingsBankAccount.CalculateInterest(1000)}");
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.