Thursday, May 28, 2020

SOLID Design Principles

SOLID is an acronym for 5 important design principles when doing OOP (Object Oriented Programming).
The intention of these principles is to make software designs more understandable, easier to maintain and easier to extend.

S —Single responsibility principle

In programming, the Single Responsibility Principle states that every module or class should have responsibility over a single part of the functionality provided by the software.

You may have heard the quote: “Do one thing and do it well”.
This refers to the single responsibility principle.


Let’s do an example of how to write a piece of code that violates this principle.

class Account
{
void CreateAccount(Database db, obj accDetails)
{
try
{
db.Add(accDetails);
}
catch (Exception ex)
{
db.LogError("An error occured: ", ex.ToString());
File.WriteAllText("\LocalErrors.txt", ex.ToString());
}
}
}

We notice how the  method has too much responsibility, given that it can both create a new account, log an error in the database, and log an error in a local file.
This violates the single responsibility principle.

Let’s try to correct it.

class Account
{
private ErrorLogger errorLogger = new ErrorLogger();
void CreateAccount(Database db, obj accDetails)
{
try
{
db.Add(accDetails);
}
catch (Exception ex)
{
errorLogger.log(ex.ToString())
}
}
}
class ErrorLogger
{
void log(string error)
{
db.LogError("An error occured: ", error);
File.WriteAllText("\LocalErrors.txt", error);
}
}
By abstracting the functionality that handles the error logging, we no longer violate the single responsibility principle.
Now we have two classes that each has one responsibility; to create a post and to log an error, respectively.

O — Open/closed principle

In programming, the open/closed principle states that software entities (classes, modules, functions, etc.) should be open for extensions, but closed for modification.

Let’s take an example of bank accounts like regular savings, salary saving, corporate etc. for different customers. As for each customer type, there are different rules and different interest rates. The code below violates OCP principle if the bank introduces a new Account type. Said code modifies this method for adding a new account type.

public class Account
{
 public decimal Interest { get; set; }
 public decimal Balance { get; set; }
 // members and function declaration
 public decimal CalcInterest(string accType)
 {

 if (accType == "Regular") // savings
 {
 Interest = (Balance * 4) / 100;
 if (Balance < 1000) Interest -= (Balance * 2) / 100;
 if (Balance < 50000) Interest += (Balance * 4) / 100;
 }
 else if (accType == "Salary") // salary savings
 {
 Interest = (Balance * 5) / 100;
 }
 else if (accType == "Corporate") // Corporate
 {
 Interest = (Balance * 3) / 100;
 }
 return Interest;
 }
}

We can apply OCP by using interface, abstract class, abstract methods and virtual methods when you want to extend functionality. Here I have used interface for example only but you can go as per your requirement.

 interface IAccount
{
 // members and function declaration, properties
 decimal Balance { get; set; }
 decimal CalcInterest();
}
 
//regular savings account 
public class RegularSavingAccount : IAccount
{
 public decimal Balance { get; set; } = 0;
 public decimal CalcInterest()
 {
 decimal Interest = (Balance * 4) / 100;
 if (Balance < 1000) Interest -= (Balance * 2) / 100;
 if (Balance < 50000) Interest += (Balance * 4) / 100;
 
 return Interest;
 }
}
 
//Salary savings account 
public class SalarySavingAccount : IAccount
{
 public decimal Balance { get; set; } = 0;
 public decimal CalcInterest()
 {
 decimal Interest = (Balance * 5) / 100;
 return Interest;
 }
}
 
//Corporate Account
public class CorporateAccount : IAccount
{
 public decimal Balance { get; set; } = 0;
 public decimal CalcInterest()
 {
 decimal Interest = (Balance * 3) / 100;
 return Interest;
 }
}

In the above code three new classes are created; regular saving account, SalarySavingAccount, and CorporateAccount, by extending them from IAccount.

This solves the problem of modification of class and by extending interface, we can extend functionality.

Above code is implementing both OCP and SRP principle, as each class has single is doing a single task and we are not modifying class and only doing an extension.

Liskov Substitution Principle (LSP)

Definition by Robert C. Martin: Functions that use pointers or references to base classes must be able to use objects of derived classes without knowin

No comments:

Post a Comment