在SOLID中,SRP和ISP的区别是什么?(单一职责原则和接口分离原则)



SOLID的"接口隔离原则"与"单一职责原则"有何不同?

SOLID的维基百科条目说

ISP将非常大的接口分成更小更具体的接口,这样客户端只需要知道他们感兴趣的方法

然而,对我来说,这听起来就像将SRP应用于接口和类一样。毕竟,如果一个接口只负责一个概念上的事情,那么你将无法进一步分解它。

是我错过了什么,还是ISP与SRP有点多余?如果没有,那么ISP暗示SRP没有什么?

SRP告诉我们在一个模块中应该只有一个职责。

ISP告诉我们,你不应该被迫面对比你实际需要的更多。如果你想使用接口Iprint()方法,你不应该实例化SwimmingPoolDriveThru类。

更具体地说,直截了当地说,他们是对同一个想法的不同看法——SRP更侧重于设计师的观点,而ISP更侧重于客户端的观点。所以你基本上是对的。

一切都来自

ISP最早是由Robert C. Martin在做为施乐公司做咨询。施乐公司发明了一种新的打印机系统能完成各种各样的任务,比如装订一套印刷文件和传真。这个系统的软件是从地面创建的启动并成功执行任务。随着软件的发展,制作修改变得越来越困难,所以即使是最小的更改将使重新部署周期缩短到一个小时。这就是它几乎不可能继续开发。设计的问题是几乎所有的任务都使用一个主要的Job类。在任何一个打印工作或装订工作必须完成,打电话给一些人方法。这导致了一个巨大的或"肥胖"的班级针对各种不同客户的多种方法。由于这种设计,订书钉工将知道所有的方法打印作业,即使它们没有用处。

Martin提出的解决方案就是所谓的接口今天的隔离原则。应用到施乐软件,一层一层的添加了Job类与其所有客户机之间的接口使用依赖倒置原则。而不是一个大的已创建作业类、订书钉作业接口或打印作业接口分别由Staple或Print类使用的,调用Job类的方法。因此,只创建了一个接口对于每个作业,这些都是由job类实现的

@ http://en.wikipedia.org/wiki/Interface_segregation_principle#Origin

SRP关心的是模块做什么,以及它是如何完成的,不允许任何抽象级别的混合。基本上,只要一个组件可以用一个句子广泛地定义,它就不会打破SRP。

另一方面,ISP关心模块应该如何被消费,是否只消费模块的一部分而忽略某些方面是有意义的。

作为一个保持精神或SRP,但可以破坏ISP的代码的例子是Facade模式。它有一个单一的责任,"提供对更大子系统的简化访问",但是如果底层子系统需要公开非常不同的想法,它确实会破坏ISP。

也就是说,通常当一段代码违反了SOLID原则时,它通常会破坏整个原则。打破特定原则,同时保留其他原则的具体例子在野外是罕见的。

罗伯特·马丁于2018年5月16日在推特上写道:

ISP可以看作类似于接口的SRP;但它不止于此。ISP概括为:"不要依赖超出你需要的东西。"SRP概括为"将在同一时间因相同原因发生变化的事物聚集在一起。"

想象一个同时具有push和pop的堆栈类。想象一个只推的客户。如果该客户端依赖于堆栈接口,那么它依赖于pop,这是它不需要的。SRP不会把push和pop分开;ISP。

SRP和ISP最终归结为同样的事情。实现它们中的任何一个都需要拆分类或接口。

然而,在其他方面存在差异。

  1. 违反SRP会对整个设计结构产生深远的影响,导致可维护性差,重用性差,当然还有低内聚和耦合。
  2. SRP对对象结构的行为成分和结构成分都有影响。
  3. 违反SRP的重新设计需要更深入的分析,需要以整体的方式看待设计的不同组成部分。

违反ISP主要是由于可读性差(在某种程度上,低内聚)。但是对维护和代码重用的影响远没有SRP那么险恶。

此外,重构代码以符合ISP的构造,似乎只是一个结构上的改变。

参见我关于SRP和ISP的博客

从我的理解来看,这两个原则是互补的,即它们需要结合在一起。

违反ISP的最终后果是变得脆弱,"鸟枪手术";或者是"蝴蝶效应"。许多代码可能会中断或需要代码更新,因为它们依赖于一些提供了超出需求的接口或对象。

违反SRP的后果主要是降低可读性和可维护性。缺乏清晰的代码结构可能需要人们在代码库中搜索(单个职责过于分散)或在单个大单元中搜索(多个职责混杂在一起)以进行一致的更改。一般来说,完全理解某些代码片段的关注点(目的)会增加开销。

这样,这两个原则就像同一变更管理的下界和上界。

在没有ISP的情况下满足RSP的例子——就像其他答案提供的那样——表达了可以有真正属于一起的代码(就像引用Robert C. Martin的堆栈例子)。但它可能做得太多,被过度设计,等等。也许在非常小的示例中,效果是不可见的,但如果它变得很大,在(间接)依赖项中的一些不相关的部分被更改后,让依赖类仍然正确编译可能会更舒服。而不是因为不相关的东西被改变而不再编译。

最新更新