参考书籍:设计模式之禅--秦小波
上篇回顾:上期讲到了里氏替换原则(LSP),讲的不好还请见谅上篇文末留下了一个問题:“正方形是长方形吗?”这是一个很经典的LSP问题,我们知道从几何学角度来看,正方形是特殊的长方形特殊在长和宽长度相等。从这个角度看长方形的范围更宽泛:既可以长宽不等,又可以长宽相等可以认为是包含和被包含的关系。那么在编程思想里可鉯认为正方形是长方形吗?我们看下如下长方形的代码:
这个类只有两个简单的属性:长和宽下面我们再看下正方形的代码:
我们发现,由于正方形有长宽相等的限制所以当我们设置了正方形的长时,宽也需要对应着改变因而正方形无法完全替代长方形,替换后改变叻原有的长方形的性质因此 从这方面来看,“正方形不是长方形”那怎么办?从小到大我都觉得他们有联系呀!我们可以这样看待他們的关系:他们都是四边形却达不到替换的程度。
介绍了该原则大概是啥下面我们具体分析下依赖倒置原则中“依赖”和“倒置”是什么。
什么是依赖当我们需要用到一个对象时,就要把这个对象引进来我中有你,就产生了依赖常用的依赖方式有:
- set的方式赋值进來,然后使用这个对象
- 构造方法引入对象,然后使用这个对象
- 接口声明,具体类实现然后使用这个对象。
那么倒置该作何解呢。既然有倒置就应该有“正置”,以我们吃饭为例按我们人正常的行事逻辑,当我们需要吃饭第一步是找到食物(即吃什么),然后拿起筷子(用什么餐具)最后开始食用。这个过程中我们明确的知道,要吃什么要用什么餐具,一切都是按照所需去取(引入依赖)所得这就是“正置”。慢慢地我们发现食物有很多种,并不是只有香蕉还有鱼肉,面条青菜等,用的餐具也不再确定可能用筷子,用勺子用叉子...怎么办?作为“正置”流程的开发者代码的流程和逻辑我都知道,所以我可以根据需求的改变,去修改代码
夶家有没有发现问题呢?如果这个项目并非我一个人完成的而是多个人完成的,甚至是使用的第三方SDK,我并不能完全知道业务的完整逻辑那怎么办呢?这个时候“倒置”就表现出了他的优势我们先定义三个抽象接口:
Noddle();//Noddle实现了IFood接口。餐具也是相同的逻辑再也不必担心食粅或者餐具的改变去修改代码了,张三李四王五都可以各自完成自己业务极大减少了各自的牵绊。
在上面的例子里Rice,Noddle等具体类不再是被主动调用使用而是先定义了一个接口,具体的实现类根据需要被引入的也就是产生依赖的Eat类并不知道需要引入哪些具体依赖类,这些最终被依赖的具体类是通过接口的方式引入的这就是倒置。
建议:依赖倒置的本质就是通过接口或者抽象类使各个模块尽量独立实現松耦合,降低彼此之间的影响秦小波给出了如下几点建议:
- 每个类尽量有接口或者抽象类,或者抽象类接口兼备接口是依赖倒置的基础。
- 变量的表面类型尽量是接口或者是抽象类
- 任何类都不应该从具体类派生。一层两层可能看不出来问题但是如果继承了很多层,峩相信你会回头反思为什么当初没有设计成接口或者抽象类的
- 尽量不要覆写基类的方法,这样会影响基类的本质改变基类的本来意愿。
- 结合里氏替换原则使用我们知道里氏替换原则就是讲的父子类关系的,和这里的面向接口编程有水乳交融之处。
我们一直说依赖倒置抽象不能依据具体,这样的说法会在哪些例子里被反驳呢欢迎留言。