Understand Single Responsibility and Interface Segregation






4.72/5 (22 votes)
This Article will help you to better understand the SRP (single responsibility principle) and ISP(Interface Segregation principle)
Introduction
Here I am going to discuss the Single responsibility and Interface segregation principle of S
LID . Now what does SOLID means ? SOLID is object oriented design principle , Where each letter has it own meaning O
- S-> Single responsibility
- O-> Open Closed
- L-> Liskov substitution
- I-> Interface segregation
- D-> Dependency inversion
According to Wikipedia the definition of SOLID is
"SOLID are guidelines that can be applied while working on software to remove code smells by causing the programmer to refactor the software's source code until it is both legible and extensible."
Background
If you read my previous article it will be very helpful for you to understand this one because i am going to use the same example here . So read the below article first.
Understand Open Closed Principle and Dependecy Inversion .
Using the code
Before start technical discussion i want to answer the below questions:
- What is Single Responsibility principle?
- Answer: " Every class should have a single responsibility, "
- What is Interface segregation ?
- Answer: "No client should be forced to depend on methods it does not use"
I am going to use the example which i use on my previous article . Let's consider an example to make it better understand. Suppose I need the price of computer. I went to a Computer shop and ask for price of Laptop computer . Let's convert my requirements into code.
public interface IComputerDescription
{
string GetDescription();
string GetColor();
double GetPrice();
double CalculatePriceAfterTax();
}
public class Desktop : IComputerDescription
{
double desktopPrice = 110;
public string GetDescription()
{
return " You get a Desktop";
}
public string GetColor()
{
return " Color is White";
}
public double GetPrice()
{
return desktopPrice;
}
public double CalculatePriceAfterTax()
{
return desktopPrice + desktopPrice * .20;
}
}
public class Laptop : IComputerDescription
{
double laptopPrice = 200;
public string GetDescription()
{
return " You get a Laptop";
}
public string GetColor()
{
return " Color is Black and Red";
}
public double GetPrice()
{
return laptopPrice;
}
public double CalculatePriceAfterTax()
{
return laptopPrice + laptopPrice * .20;
}
}
public class Tablet : IComputerDescription
{
double tabletPrice = 500;
public string GetDescription()
{
return " You get a Tablet";
}
public string GetColor()
{
return " Color is Silver";
}
public double GetPrice()
{
return tabletPrice;
}
public double CalculatePriceAfterTax()
{
return tabletPrice + tabletPrice * .20;
}
}
public class GiftItem : IComputerDescription
{
public string GetDescription()
{
return "Yes you get one PenDrive as gift item enjoy";
}
public string GetColor()
{
return "Choose Any color from Red Black and White";
}
public double GetPrice()
{
return 0;
}
public double CalculatePriceAfterTax()
{
return 0;
}
}
public class Shop
{
public string GetMyComputer(IComputerDescription cmptype)
{
// No matter how many types of computer comes
var myComp = cmptype.GetDescription();
return myComp;
}
public string GetMyComputerPrice(IComputerDescription cmptype)
{
var myCompprice = "Price is : " + cmptype.CalculatePriceAfterTax().ToString();
return myCompprice;
}
public string GetAvailableColor(IComputerDescription cmptype)
{
var myCompcolor = cmptype.GetColor().ToString();
return myCompcolor;
}
public string WhatIsThecolorofGiftItem(IComputerDescription cmptype)
{
return GetAvailableColor(cmptype);
}
public string IsThereAnyGiftItem(IComputerDescription gftType)
{
return GetMyComputer(gftType);
}
}
So i ask for laptop price right.
var computerShop = new Shop();
computerShop.GetMyComputerPrice(new Laptop());
If you run the code it will execute fine and give you the output "Price is : 240".
What are the available colors for the laptop ? The answer is : "Color is Black and Red"
computerShop.GetAvailableColor(new Laptop());
Then I ask again what if i buy a laptop , will i get any gift or something free item ? Again converting human language to code
computerShop.IsThereAnyGiftItem(new GiftItem());
This will give you the output "Yes you get one PenDrive as gift item enjoy "
Normal human behavior is when we get any gift we get excited. I also become so excited an ask another question from that excitement . What are the available colors of your gift pen drive ? The answer is : "Choose Any color from Red Black and White".
computerShop.WhatAreTheColorofGiftItem(new GiftItem());
I take the decision that i will buy a black laptop and want to take white color pen drive.Just notice the code and Wait Wait (Comes on my mind) !! (I start thinking ) Why , what happen whats wrong ? After analyzing the code i get the answer . Nothing wrong to buy laptop and taking pen drive . But our code violating the rule of SRP(Single Responsibility principle) and ISP( Interface segregation)
Did you notice ? No, Still confuse ? Ok No problem i will explain.
How we violate the SRP rule?
" There should never be more than one reason for a class to change." — Robert Martin, SRP paper linked from The Principles of OOD "
Look at the functions below those are the culprits , its a price calculation logic but its in the desktop
and laptop
tablet
class . So if the price has changed then we need to change the classes for that and if the calculation logic changes then we need to do it again . So we need to change the same class for two different reason , because classes have multiple responsibility ( this is the violation).
public double CalculatePriceAfterTax()
{
return desktopPrice+ (desktopPrice *0.2);
}
public double CalculatePriceAfterTax()
{
return laptopPrice + (laptopPrice * 0.2);
}
public double CalculatePriceAfterTax()
{
return tabletPrice+(tabletPrice*.20);
}
Now See how violation take place on Desktop
class for SRP
public class Desktop : IComputerDescription
{
double desktopPrice = 130; // Change Price (one reason)
public string GetDescription()
{
return " You get a Desktop";
}
public string GetColor()
{
return " Color is White";
}
public double GetPrice()
{
return desktopPrice;
}
public double CalculatePriceAfterTax()
// Change calculation Logic (another different reason )
{
return desktopPrice +
(desktopPrice * .20)+1; // Violation of SRP
}
}
How we violate ISP rule?
"Clients should not be forced to depend upon interfaces that they do not use." — Robert Martin, ISP paper linked from The Principles of OOD
Yes we did it by implementing the interface to the GiftItem
. Because gift items are free no price calculation needed but we forcefully implement it. (Violation of ISP).
public class GiftItem : IComputerDescription
{
public string GetDescription()
{
return "Yes you get one PenDrive as gift item enjoy";
}
public string GetColor()
{
return " Any color from Red Black and White";
}
public double GetPrice()
{
return 0; // No Use here but forcefully implemented ------ Violation of ISP
}
public double CalculatePriceAfterTax()
{
return 0; // No Use here but forcefully implemented ------ Violation of ISP
}
}
Did you notice the violation ? Yes we modify the class for two different reason and unnecessarily implement the interface method CalculatePriceAfterTax
and GetPrice
on GiftItem
class, Now lets follow the SRP & ISP rule and implement a new structure . If we can give the responsibility of calculating the price to another class and remove the unused implementation from GiftItem
class then we can achieve our goal .
Look at the code below how we break down the interface ( this helps to implement ISP rule easily)
public interface IComputerDescription
{
string GetDescription();
string GetColor();
}
public interface IComputerPrice
{
double GetPrice();
}
public interface IPriceCalculation
{
double CalculatePriceAfterTax(IComputerPrice c);
}
Now the implementation also going to be changed right .
public class Desktop : IComputerDescription,IComputerPrice
{
double desktopPrice = 130;
public string GetDescription()
{
return " You get a Desktop";
}
public string GetColor()
{
return " Color is White";
}
public double GetPrice()
{
return desktopPrice;
}
}
public class Laptop : IComputerDescription,IComputerPrice
{
double laptopPrice = 200;
public string GetDescription()
{
return " You get a Laptop";
}
public string GetColor()
{
return " Color is Black";
}
public double GetPrice()
{
return laptopPrice;
}
}
public class Tablet : IComputerDescription,IComputerPrice
{
double tabletPrice = 500;
public string GetDescription()
{
return " You get a Tablet";
}
public string GetColor()
{
return " Color is Silver";
}
public double GetPrice()
{
return tabletPrice;
}
}
public class GiftItem : IComputerDescription
{
public string GetDescription()
{
return "Yes you get one PenDrive as gift item enjoy";
}
public string GetColor()
{
return " Any color from Red Black and White";
}
}
//New Class introduce to take the responsibility of calculation price
public class CalculateComputerPrice:IPriceCalculation
{
public double CalculatePriceAfterTax(IComputerPrice c)
{
return c.GetPrice() + c.GetPrice() * .20;
}
}
public class Shop
{
public string GetMyComputer(IComputerDescription cmptype)
{
// No matter how many types of computer comes
var myComp = cmptype.GetDescription();
return myComp;
}
public string GetMyComputerPrice(IPriceCalculation cmpCal, IComputerPrice cmpPrice)
{
var myCompprice = "Price is : " +
cmpCal.CalculatePriceAfterTax(cmpPrice).ToString();
return myCompprice;
}
public string GetAvailableColor(IComputerDescription cmptype)
{
var myCompcolor = cmptype.GetColor().ToString();
return myCompcolor;
}
public string WhatIsThecolorofGiftItem(IComputerDescription cmptype)
{
return GetAvailableColor(cmptype);
}
public string IsThereAnyGiftItem(IComputerDescription gftType)
{
return GetMyComputer(gftType);
}
}
Points of Interest
So if i ask the same question again with our new code structure what will be the answer :
Question 1) What's the price of a laptop?
Answer : "Price is : 240". Same as before but different class (CalculateComputerPrice
) is responsible for the calculation. So its Satisfy (SRP)
var queryforProduct = new Laptop();
var computerShop = new Shop();
var responsibleforCalculation = new CalculateComputerPrice();
var myAnswer = computerShop.GetMyComputerPrice(responsibleforCalculation, queryforProduct);
Question 2) What are the available colors for the laptop ?
Answer: "Color is Black and Red" Same as before. So coe does not effected for refactor.
var myAnswer = computerShop.GetAvailableColor(queryforProduct);
Question 3) What if i buy a laptop , will i get any gift or something free item ?
Answer : "Yes you get one PenDrive as gift item enjoy " same answer but GiftItem
classs remove unused items. So its satisfy (ISP)
public class GiftItem : IComputerDescription
{
public string GetDescription()
{
return "Yes you get one PenDrive as gift item enjoy";
}
public string GetColor()
{
return "Choose Any color from Red Black and White";
}
}
var queryforProduct = new GiftItem();
var computerShop = new Shop();
var myAnswer = computerShop.IsThereAnyGiftItem(queryforProduct);
Question 4) What are the available colors of your gift pen drive ?
Answer: "Choose Any color from Red Black and White". Same answer but GiftItem
classs remove unused items. So its satisfy(ISP)
var queryforProduct = new GiftItem();
var computerShop = new Shop();
var myAnswer = computerShop.WhatIsThecolorofGiftItem(queryforProduct);
So now our code satisfy the both SRP and ISP.