接口隔离原则的背后推理是什么?

27
接口隔离原则(ISP)指出,许多客户端特定的接口优于一个通用接口。为什么这很重要?
7个回答

34

ISP声明:

客户不应被迫依赖于他们不使用的方法。

ISP涉及到重要特征- 内聚性耦合度
理想情况下,您的组件必须高度定制化。这会提高代码的健壮性和可维护性。

实施ISP会带来以下好处:

  • 内聚性 - 更好的可理解性、健壮性
  • 耦合度 - 更好的可维护性,高抗变性

如果您想了解更多关于软件设计原则的信息,请获取《敏捷软件开发:原则、模式与实践》一书。


2
如果你是C#迷,这本书也有C#版本。它叫做《敏捷原则、模式与实践(C#版)》。 - Erik van Brakel

10
界面隔离原则是SOLID原则中的“I”,在深入了解该原则之前,让我们先解释一下“SOLID”是什么意思。
可以将SOLID视为一组最佳实践和专家建议(这意味着它们已被证明有效),旨在为我们设计应用程序提供可靠的基础。这些实践旨在使我们的应用程序更易于维护、扩展、适应和扩展。
首先,您必须意识到您不会永远停留在当前位置。如果我们使用标准和众所周知的架构,我们可��确保我们的代码将易于由接替我们的其他开发人员维护,我相信您不想处理修复没有应用任何已知方法论的代码的任务,因为这样很难理解它。
界面隔离原则:客户端不应被强制实现他们不会使用的不必要方法。这意味着有时我们倾向于创建具有许多方法的接口,这在一定程度上可能是好的,但是这很容易被滥用,并且我们最终会得到实现空或无用方法的类,这当然会给我们的应用程序增加额外的代码和负担。如果你像视觉辅助工具那样声明很多方法在单个接口中,那么实现一个接口但只需要其中几种方法的类将如下所示:
另一方面,如果您正确应用界面隔离并将其拆分为较小的子集,则可以确保仅实现所需的方法:
看!效果好多了!实施此原则将允许您具有低耦合度,这有助于更好地维护和高抗变性。因此,您可以真正利用接口的使用,并在确实需要时实现方法。现在让我们来看一个不太抽象的例子,假设您声明了一个名为“Reportable”的接口。
public interface Reportable {

        void printPDF();
        void printWord();
        void printExcel();
        void printPPT();
        void printHTML();


}

如果您有一个只能将某些数据导出为Excel格式的客户端,您可以实现接口,但您是否只需要实现excel方法?答案是否定的,即使您不打算使用所有方法,您仍需要编写所有方法的实现,这可能会导致大量的垃圾代码,从而使代码难以维护。

请记住保持简单,不要重复自己,您会发现自己已经在使用这个原则而不知道。


值得注意的是,“厨房水槽”接口并非完全没有优点,因为需要存储一些“可报告”的事物列表并稍后将它们提供给消费者的代码不必担心消费者需要哪些功能,只要任何实际实例可以处理消费者的需求即可。并不是说专注的接口通常都是好的,但有一些较大的接口也是有好处的。 - supercat

6

它简化了任何一个客户端使用的界面,并消除了他们可能在不需要的界面部分上开发的依赖。


4

Robert Martin's paper关于这个主题提供了一个较少提到的解释:

客户端对接口施加的向后力。

如果两个类直接依赖于第三个类的两种不同方法,则增加了更改前两个类中任何一个的可能性会影响另一个类的可能性。

假设我们有三个类:RedGreenBlue

RedGreen都依赖于Blue,但每个类依赖于不同的方法。这意味着Red依赖于Blue的一种方法,但不使用另一种方法。同样,Green依赖于Blue,但仅使用其中一种方法而不是另一种方法。

该原则的违反在 红色绿色 中,因为每个类都依赖于一个名为 蓝色 的类,但却没有使用它至少一个方法。
这可能会造成什么问题?
  • 我需要更改 红色,并且我还需要更改 蓝色 以适应 红色 的需求。
  • 我没有更改 绿色 所依赖的 蓝色 中的具体方法,但是,绿色 仍然依赖于 蓝色,而我已经更改了 蓝色,这可能仍会影响到 绿色
  • 因此,我对 红色 的更改有可能会影响到 蓝色,因为它们都依赖于同一个类。
这就是“反向作用力”。我们有时会改变一个类,因为它的客户端的需求。如果那个类有不同的客户端用于不同的事情,我们就有可能影响到它们。
正如所述,接口隔离原则的简单定义是:

没有客户端应该被强制依赖于其未使用的方法。

从Robert Martin的论文中可以看出,许多ISP的解释实际上是在谈论其他原则。

  • 具有大量方法的类或接口是不可取的,但并非专门因为ISP而违反单一职责。但是,如果类不使用所有方法,则ISP的违规不在大接口或大类中 - 而是在依赖于大接口的类中。如果它们使用所有方法,这仍然听起来很混乱,但这与ISP无关。
  • 实现接口但对某些方法抛出异常的类是不好的,但这也不是ISP。ISP关注的是依赖于接口的类,而不是实现接口的类。

如果我们在Google中搜索“接口隔离”,大多数包含代码示例的前几个结果都展示了未完全实现接口的类,这并不是ISP的重点。有些甚至错误地重新阐述了原则:

接口隔离原则表明,客户端不应被强制实现他们不使用的接口

...但那不是原则。定义性文件提到这些问题是违反ISP的副作用,但表明它们是Liskov替换违规。

此外,每次向基类添加新接口时,必须在派生类中实现(或允许默认)。事实上,相关做法是将这些接口添加到基类作为nil虚函数而不是纯虚函数;具体是为了避免派生类负担实现它们的需要。正如我们在本专栏的第二篇文章中所学到的,这种做法违反了Liskov替换原则(LSP),导致维护和重用问题。

更重要的是,说客户端不应该实现它不使用的方法甚至没有意义。一个接口的客户端不会实现它使用或不使用的方法-它们消费其方法。 List<E> 的客户端不会实现 List<E> 的方法和属性。它调用 List<E> 的方法和属性。

我并不是要傲慢地将这篇论文当作圣经一样引用。但如果我们要使用该文章中描述的原则名称(即文章本身的名称),那么我们也应该考虑该文章中实际的定义和解释。

4

一个原因是每个接口仅有少量方法,这样拥有多个接口会更容易实现每个接口并正确实现。一个大的接口可能会很难处理。此外,在某个场景中使用一个专注的接口可以使代码更易于维护,因为您可以看到对象的哪个方面正在被使用(例如,IComparable接口让您知道该对象仅在给定场景中用于比较)。


3
这个原则主要有两个目的:
  • 使代码更易读和可管理。

  • 促进类的单一职责(高内聚)。当然,一个类为什么要有没有行为影响的方法?为什么不直接删除它?这就是ISP的作用。

设计人员在ISP方面必须问自己几个问题:
  • ISP能实现什么目标?
  • 如何分析已有代码是否违反了ISP?
需要进一步讨论的是,这个原则并不是严格意义上的“原则”,因为在某些情况下,将ISP应用于设计,而不是提高可读性,可能会使对象结构变得难以阅读,并且充斥着不必要的代码。你可以在java.awt.event包中观察到这一点。
更多内容请参考我的博客:http://design-principle-pattern.blogspot.in/2013/12/interface-segregation-principle.html

2

ISP很重要。

ISP的基本思想是:客户端不应被迫依赖于它不使用的方法。

这个原则似乎更加合理。理想情况下,客户端不应实现它不使用的方法。

请参阅下面的SE问题以获取代码示例:

接口隔离原则-对接口进行编程

优点:

  1. 灵活性:在没有ISP的情况下,你有一个通用FAT接口和许多实现它的类。假设你有1个接口和50个类。如果接口发生变化,所有50个类都必须更改它们的实现。

    采用ISP后,您将把通用FAT接口分成细粒度的小接口。如果小细粒度接口发生变化,则只会影响实现该接口的类。

  2. 可维护性和易用性:由于更改仅限于细粒度接口而不是通用FAT接口,因此代码维护更容易。不相关的代码不再是实现类的一部分。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接