Solid Principles

 SOLID Principles is an acronyms of 

  • Single Responsibilities Principle
  • Open Closed Principles
  • Liskov Substitution Principles
  • Interface Segregation Principles
  • Dependency Inversion Principles

I am going to explain all the five principles with the same example. Suppose we are creating the mobile banking app. So, we are implementing the functionality of payment. 

Single Responsibilities Principle:- This principles states that each class should be responsible for one functionality or module through out the application.

Example:- We are implementing Send Money, Receive Money and Add Beneficiary inside payment module. Now client want the functionality "Apply for Credit Card" in mobile banking app. We are not adding the "Apply for credit card" functionality in payment module. We should create a new module and in that module we should create the new functionality i.e. "Apply for Credit Card".

public interface Ipayment { void SendMoney(); void ReceiveMoney(); void AddBeneficiary(); } public class OnlinePayment : Ipayment { public void SendMoney() { } public void ReceiveMoney() { } public void AddBeneficiary() { } } public interface ICreditCard { void ApplyForCreditCard(); } public class CreditCard : ICreditCard { public void ApplyForCreditCard() { } }

Open Closed Principle:- This principle states that a class or module should be open for extension and closed for modification.

Example:- Now client want to add the functionality of cheque Payment in mobile banking app. So we are using the same payment interface and while using that interface, we must use the AddBeneficiary method. This is voilated the Interface Segregate Principle but for some time let's ignore that part and understand the OCP. 

Now if we don't use OCP and we are modified in existing class. Let take us look on below example how we can add the cheque functionality in existing class

public interface Ipayment { void SendMoney(); void ReceiveMoney(); void AddBeneficiary(); } public class Payment : Ipayment { private string _istrPayment; public Payment(string astrPaymentType) { this._istrPayment = astrPaymentType; } public void SendMoney() { if(this._istrPayment == "Online") { } if(this._istrPayment == "Cheque") { } } public void ReceiveMoney() { if(this._istrPayment == "Online") { } if(this._istrPayment == "Cheque") { } } public void AddBeneficiary() { if(this._istrPayment == "Online") { } } } public class Program { public static void Main() { Payment lpayment = new Payment("Online"); lpayment.SendMoney(); lpayment = new Payment("Check"); lpayment.SendMoney(); } }

The above example shows that we are updating the existing class that voilates the open closed principle. So we should create a new class for cheque payment and should be closed for Online payment. 

Now take a look on above example with the use of OCP principle.

public interface Ipayment { void SendMoney(); void ReceiveMoney(); void AddBeneficiary(); } public class OnlinePayment : Ipayment { public void SendMoney() { } public void ReceiveMoney() { } public void AddBeneficiary() { } } public class ChequePayment : Ipayment { public void SendMoney() { } public void ReceiveMoney() { } public void AddBeneficiary() { // Not implemented.. Throw error } } public class Program { public static void Main() { Payment lpayment = new OnlinePayment(); lpayment.SendMoney(); lpayment = new ChequePayment(); lpayment.SendMoney(); } }

Liskov Substitution Principle:-  This principles states that base class should be replaced the child class without affecting the base class functionality.

Example:- Now we introduce the foreign payment method (transaction on foreign countries). This foreign payment is also come under the online transaction payment. So our first approach is like we should inherit the onlinepayment class but if we are doing so then we are voilating the Liskov principles.

Let take an example how we are voilating the principles if we inheriting the foreignpayment to onlinepayment.

public interface Ipayment { void SendMoney(); void ReceiveMoney(); void AddBeneficiary(); } public class OnlinePayment : Ipayment { public virtual void SendMoney() { Console.WriteLine("Base"); } public virtual void ReceiveMoney() { } public virtual void AddBeneficiary() { } } public class ForeignOnlinePayment : OnlinePayment { public override void SendMoney() { Console.WriteLine("Child"); } public override void ReceiveMoney() { } public override void AddBeneficiary() { } } public class Program { public static void Main() { OnlinePayment lOnlinePayment = new OnlinePayment(); lOnlinePayment.SendMoney(); OnlinePayment lForeignOnlinePayment = new ForeignOnlinePayment(); lForeignOnlinePayment.SendMoney(); } }

The above code states that ForignOnlinePayment is now changing the functionality of base class OnlinePayment. OnlinePayment have a logic of current country standards and if doing transaction in other country then we have different standards logic. Now if we call the ForignOnlinePayment class in replace of OnlinePayment then results are differents.

Now if we follow the Liskov principle then we should create a ForignOnlinePayment class and this class should be inherit to Ipayment interface.

public interface Ipayment { void SendMoney(); void ReceiveMoney(); void AddBeneficiary(); } public class ForeignOnlinePayment : Ipayment { public void SendMoney() { } public void ReceiveMoney() { } public void AddBeneficiary() { } }

Now you are thinking if we are creating a logic as shown above in such a manner then we can never do inherit the base class to the child class.

Let me introduce the one more functionality i.e. "Pay Your Contact" in mobile app. In this functionality we can make transaction for those who are already in our contact list.


public class Program { public static void Main() { OnlinePayment lOnlinePayment = new OnlinePayment(); lOnlinePayment.SendMoney(); OnlinePayment lForeignOnlinePayment = new PayContactOnlinePayment(); lForeignOnlinePayment.SendMoney(); } } public interface Ipayment { void SendMoney(); void ReceiveMoney(); void AddBeneficiary(); } public class OnlinePayment : Ipayment { public virtual void SendMoney() { Console.WriteLine("Base"); } public virtual void ReceiveMoney() { } public virtual void AddBeneficiary() { } } public class PayContactOnlinePayment : OnlinePayment { public override void SendMoney() { } public override void ReceiveMoney() { } public override void AddBeneficiary() { } }

Now in above example shows that the same result are coming when we provide the input contact list user to the PayContactOnlinePayment class and OnlinePayment class.

In this way we will follow the Liskov substitution principles.


Interface Segregation Principles:- This principles states that clients should not forced to implement interface that they cannot use.

Example:- Now, I am taking the same example as we look on open closed principles. In there example we are forcing to client in cheque payment class to implement the AddBeneficiary Method. I am showing a little look of ChequePayment class which is given below

public interface Ipayment { void SendMoney(); void ReceiveMoney(); void AddBeneficiary(); } public class ChequePayment : Ipayment { public void SendMoney() { } public void ReceiveMoney() { } public void AddBeneficiary() { // Not implemented.. Throw error } } public class Program { public static void Main() { lpayment = new ChequePayment(); lpayment.SendMoney(); } }

We should create a different interface whose name is IBeneficiary instead of using AddBeneficiary in ChequePayment class. Now, Let take a look how we can use interface segregate in above code.

public interface Ipayment { void SendMoney(); void ReceiveMoney(); } public interface IBeneficiary { void AddBeneficiary(); } public class OnlinePayment : Ipayment, IBeneficiary { public void SendMoney() { } public void ReceiveMoney() { } public void AddBeneficiary() { } } public class ChequePayment : Ipayment { public void SendMoney() { } public void ReceiveMoney() { } }

So, In above code we are dividing a big interface into two small interface and by doing so we are not forcing the client to implement the code which is not use.

Dependency Inversion Principle:- This principle states that high level module or class should not be depend on low level class or module. It should be depend on abstraction.

Example:- Now let take a look of the previous example in some deeper way. Suppose while adding beneficiary of any user then client wants to add their beneficiaries in SQL as well as store in excel sheet.

Let take an example how we can use the logic if we not using Dependency Inversion Principle.

public interface Ipayment { void SendMoney(); void ReceiveMoney(); } public interface IBeneficiary { void AddBeneficiary(); } public class OnlinePayment : Ipayment, IBeneficiary { public void SendMoney() { } public void ReceiveMoney() { } public void AddBeneficiary() { DBFunction lDBFunction = new DBFunction(); lDBFunction.AddBeneficiaryData(); ExcelFunction lExcelFunction = new ExcelFunction(); lExcelFunction.AddBeneficiaryData(); } } public class DBFunction { public void AddBeneficiaryData() { } } public class ExcelFunction { public void AddBeneficiaryData() { } }

In above example we can see that how we are tightly coupled with DBFunction and ExcelFunction. Suppose in future, if we want to add data in different place like MSSQL or Text File then we are creating different class and methods and modify the existing code by adding the calling of the TextFile code.

So if we want to ignore this tightly coupled code then we must have to use the abstraction in lower module or classes and then we injecting the instances of lower class or module in higher class or module.

Let take a another look in code.

public interface Ipayment { void SendMoney(); void ReceiveMoney(); } public interface IBeneficiary { void AddBeneficiary(); } public class OnlinePayment : Ipayment, IBeneficiary { private IStoreData iIStoreData; public OnlinePayment(IStoreData aStoreData) { this.iIStoreData = aStoreData; } public void SendMoney() { } public void ReceiveMoney() { } public void AddBeneficiary() { iIStoreData.AddBeneficiaryData(); } } public interface IStoreData { void AddBeneficiaryData(); } public class DBFunction : IStoreData { public void AddBeneficiaryData() { } } public class ExcelFunction : IStoreData { public void AddBeneficiaryData() { } } public class Program { public static void Main() { OnlinePayment lOnlinePayment = new OnlinePayment(new DBFunction()); lOnlinePayment.AddBeneficiary(); lOnlinePayment = new OnlinePayment(new ExcelFunction()); lOnlinePayment.AddBeneficiary(); } }

In the above example DBFunction and ExcelFunction should be depend on abstraction i.e. interface IStoreData and we are injecting the functionality through constructor in higher module. Now client want to add the functionality whichever they want.

So done from my side. I hope you enjoyed this article.

No comments:

Post a Comment