Software Architecture Cheat Blog 3: Open Closed Principle





5.00/5 (1 vote)
Open Closed Principle (OCP) states that software entities (classes, modules, functions, etc.) should be open for extension but closed for modifications.
Define Principle
Open Closed Principle (OCP) states that software entities (classes, modules, functions, etc.) should be open for extension but closed for modifications.
Violation Example
Let us consider a simple banking module that is responsible for calculating
the interest of accounts with respect to the account type. Say the app owner bank offers to their clients
three types of accounts: savings account, basic checking account, money market deposit account. Each of the account types has
a different interest rate, and the interest
is calculated on other factors such as amount deposited, average balance per month, and so on. Thus based on these business rules we design this class
Accounts
which calculates the interest based on the account type.
public enum AccountTYpe
{
Savings,
Checking,
MoneyMarket
}
public class Accounts
{
private AccountTYpe accountType { get; set; }
public Decimal CalculateInterest()
{
Decimal interest = 0;
if (accountType == AccountTYpe.Savings)
{
//consider business rules and calculate interest of savings account
interest = 2.50m;
}
else if (accountType == AccountTYpe.Checking)
{
//consider business rules and calculate interest of checking account
interest = 3.50m;
}
else
{
//consider business rules and calculate interest of money market account
interest = 5.00m;
}
return interest;
}
}
The above solution works perfectly for our client bank and the app went live and everyone was happy. A few months later the bank realized other banks are making money from certificates of deposit accounts, so we should do that too, so let's change our system to accommodate that. Now our revised system looks like this:
public enum AccountTYpe
{
Savings,
Checking,
MoneyMarket,
CertificatesOfDeposit
}
public class Accounts
{
private AccountTYpe accountType { get; set; }
public Decimal CalculateInterest()
{
Decimal interest = 0;
if (accountType == AccountTYpe.Savings)
{
//consider business rules and calculate interest of savings account
interest = 2.50m;
}
else if (accountType == AccountTYpe.Checking)
{
//consider business rules and calculate interest of checking account
interest = 3.50m;
}
else if (accountType == AccountTYpe.CertificatesOfDeposit)
{
//consider business rules and calculate interest of certificates of deposit account
interest = 6.35m;
}
else
{
//consider business rules and calculate interest of money market account
interest = 5.00m;
}
return interest;
}
}
Considering the above scenario it is not hard to guess, it will go through the same process each time the bank wants to launch another account type or change an existing account type's business rules. This is where the open closed principle is violated.
Resolution
To solve this we can create a common interface which will be implemented by all classes that represent the account types. The benefit is future changes can be accommodated better without modifying the corresponding class.
public interface IAccount
{
Decimal CalculateInterest();
}
public class SavingsAccounts:IAccount
{
public decimal CalculateInterest()
{
return 2.50m;
}
}
public class CheckingAccounts : IAccount
{
public decimal CalculateInterest()
{
return 3.50m;
}
}
public class MoneyMarketAccounts : IAccount
{
public decimal CalculateInterest()
{
return 5.00m;
}
}
Using this approach we can add as many account types as needed, all we need is
to add another class for that account type. Also modifications to the business logic reside
in the respective account type class. Thus the IAccount
interface implements the idea of open for extension but closed for modifications.