第十章 接口隔离原则(SIP)
接口隔离原则(SIP)像它的名字,结构图图10.1。
图10.1 接口隔离原则
如图10.1的情况,血多users调用了OPS类中的操作,假设User1只用了op1,User2只用了op2,User3只用了op3。
现在假设OPS是一个类似Java语言写成的类,很明显,User1的源码无意义的依赖于op2,op3,即使User1没有调用他们。这里的依赖意味着op2,op3源码的改变将迫使User1重新编译,重新部署,即使这里的改变跟User1没有任何关系。
这个文图可以通过把操作分割成不同的接口,如图10.2
和之前一样,我们假设这些实现是用了Java这类静态语言写的,User1的源码得依赖与U1Ops接口,和op1,但不依赖与OPS类了。因此OPS的改变,User1并不关系,也不会造成User1被重新编译,重新部署。
图10.2 隔离操作
ISP和语言
很明显,之前的描述取决与编程语言类型的特性。静态类型语言,比如Java,程序员在User里得先声明,用import,use或者include等关键字。在源码里声明之后,亦造成了源码里的依赖,使得之后被强制重新编译,重新部署。
动态类型,像Ruby和Python,在源码里不需要写声明,他们得调用是在运行时被推断出来的,因此源码里没有依赖,不需要被重新编译,重新部署。这就是相比静态语言,动态类型语言搭建的系统更加灵活,更加低耦合。
说了这些,可能令人觉得ISP描述的是语言的问题,而不是架构的问题。
ISP和架构
如果你回头看ISP的根本目的,你会发现潜在的更深层次的问题。大多时候,模块依赖于更多它并需要的东西是很不利的。在源码层面很好解释,你得重新编译,重新部署,但在架构层面上也是很不利的。
考虑这么个例子,在系统S的架构。包含了框架F,设想框架F的作者实现中绑定了特殊的数据库D,那么S依赖于F,F依赖于D。
图10.3 一个问题架构
设想D包含了F并不使用的特性,而且S并不关心,那么改变D中的这些特性也许将使得F重新部署,进而重新部署S,更糟的是,D中的这些特性要是失败了,可能造成F和S也失败了。
小结
依赖于一些你并不需要的东西可能会给你带来你意想不到的麻烦。
在第十三章 组件耦合中的公共重用原则里,我们将讨论更多细节。