理解单一职责原则和接口隔离原则






4.72/5 (22投票s)
本文将帮助您更好地理解 SRP(单一职责原则)和 ISP(接口隔离原则)
引言
在此,我将讨论 SOLID 中的单一职责和接口隔离原则。那么 SOLID 是什么意思呢?SOLID 是面向对象设计原则,其中每个字母都有其自身的含义。
- S-> 单一职责
- O-> 开闭原则
- L-> 里氏替换原则
- I-> 接口隔离原则
- D-> 依赖倒置原则
根据 维基百科,SOLID 的定义是:
"SOLID 是在处理软件时可以应用的一组指导原则,通过使程序员重构软件的源代码,直到其既可读又可扩展,从而消除代码坏味。"
背景
如果您阅读了我之前的文章,将会非常有帮助,因为我将在这里使用相同的示例。所以请先阅读下面的文章。
使用代码
在开始技术讨论之前,我想回答以下问题:
- 什么是单一职责原则?
- 答案:" 每个类都应该只有一个职责, "
- 什么是接口隔离?
- 答案: "不应强迫客户端依赖于它们不使用的接口方法"
我将使用我在上一篇文章中使用的示例。让我们考虑一个例子来更好地理解。假设我需要电脑的价格。我去了电脑商店,询问笔记本电脑的价格。让我们将我的需求转化为代码。
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);
}
}
所以我问的是笔记本电脑的价格,对吧?
var computerShop = new Shop();
computerShop.GetMyComputerPrice(new Laptop());
如果您运行代码,它将正常执行并给出输出"价格是:240"。
笔记本电脑有哪些可用颜色?答案是:"颜色是黑色和红色"
computerShop.GetAvailableColor(new Laptop());
然后我再次问,如果我买一台笔记本电脑,我会得到任何礼物或免费物品吗?再次将人类语言转化为代码
computerShop.IsThereAnyGiftItem(new GiftItem());
这将为您提供输出"是的,您会收到一个 U 盘作为礼品,请享用"
正常的行为是我们得到任何礼物都会感到兴奋。我也变得非常兴奋,并带着兴奋提出了另一个问题。您的礼品 U 盘有哪些可用颜色?答案是:"可从红色、黑色和白色中任选一种颜色"。
computerShop.WhatAreTheColorofGiftItem(new GiftItem());
我决定购买一台黑色笔记本电脑,并想要一个白色 U 盘。请注意代码,等等,等等(我想到了)!!(我开始思考)为什么,发生了什么,出了什么问题?分析代码后,我得到了答案。购买笔记本电脑和获得 U 盘没有任何问题。但我们的代码违反了 SRP(单一职责原则)和 ISP(接口隔离原则)的规则
你注意到吗?还没有,仍然感到困惑? 好的,没问题,我会解释。
我们是如何违反 SRP 规则的?
"一个类永远不应该有超过一个改变的理由。" — Robert Martin,SRP 论文,链接自 The Principles of OOD "
看看下面的函数,它们是罪魁祸首,这是价格计算逻辑,但它在 desktop
和 laptop
tablet
类中。所以,如果价格发生变化,我们就需要更改这些类,如果计算逻辑发生变化,我们就需要再次执行。因此,我们需要出于两个不同的原因更改同一个类,因为类有多个职责(这是违规)。
public double CalculatePriceAfterTax()
{
return desktopPrice+ (desktopPrice *0.2);
}
public double CalculatePriceAfterTax()
{
return laptopPrice + (laptopPrice * 0.2);
}
public double CalculatePriceAfterTax()
{
return tabletPrice+(tabletPrice*.20);
}
现在看看 Desktop
类是如何违反 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
}
}
我们是如何违反 ISP 规则的?
"客户端不应依赖于他们不使用的接口。" — Robert Martin,ISP 论文,链接自 The Principles of OOD
是的,我们通过将接口实现到 GiftItem
来做到了这一点。因为礼品是免费的,不需要计算价格,但我们强制实现了它。(违反 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
}
}
你注意到违规了吗?是的,我们因为两个不同的原因修改了类,并毫无必要地在 GiftItem
类中实现了接口方法 CalculatePriceAfterTax
和 GetPrice
。现在,让我们遵循 SRP 和 ISP 规则并实现一个新的结构。如果我们能将计算价格的职责委托给另一个类,并从 GiftItem
类中移除未使用的实现,那么我们就可以实现我们的目标。
看看下面的代码,我们是如何分解接口的(这有助于轻松实现 ISP 规则)
public interface IComputerDescription
{
string GetDescription();
string GetColor();
}
public interface IComputerPrice
{
double GetPrice();
}
public interface IPriceCalculation
{
double CalculatePriceAfterTax(IComputerPrice c);
}
那么实现也将改变,对吧?
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);
}
}
值得关注的点
所以,如果我再次用我们新的代码结构问同样的问题,答案会是什么?
问题 1) 笔记本电脑的价格是多少?
答案: "价格是:240"。 与之前相同,但由不同的类(CalculateComputerPrice
)负责计算。所以它满足(SRP)
var queryforProduct = new Laptop();
var computerShop = new Shop();
var responsibleforCalculation = new CalculateComputerPrice();
var myAnswer = computerShop.GetMyComputerPrice(responsibleforCalculation, queryforProduct);
问题 2) 笔记本电脑有哪些可用颜色?
答案:"颜色是黑色和红色" 与之前相同。因此,代码不受重构的影响。
var myAnswer = computerShop.GetAvailableColor(queryforProduct);
问题 3) 如果我买一台笔记本电脑,我会得到任何礼物或免费物品吗?
答案:"是的,您会收到一个 U 盘作为礼品,请享用" 答案相同,但 GiftItem
类移除了未使用的项目。所以它满足(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);
问题 4) 您的礼品 U 盘有哪些可用颜色?
答案:"可从红色、黑色和白色中任选一种颜色"。 答案相同,但 GiftItem
类移除了未使用的项目。所以它满足(ISP)
var queryforProduct = new GiftItem();
var computerShop = new Shop();
var myAnswer = computerShop.WhatIsThecolorofGiftItem(queryforProduct);
所以现在我们的代码同时满足 SRP 和 ISP。