依赖倒转原则又称依赖倒置原则:
抽象不应该依赖细节,细节应该依赖于抽象。说白了,就是针对接口编程,不要针对实现编程。
依赖倒置原则包含三层含义:
1)高层模块不应该依赖低层模块,两者都应该依赖其抽象;
2)抽象不应该依赖细节;
3)细节应该依赖抽象。
看了上面的解释相信大家会和我一样会有一些疑问在脑海里,下面来详细说一说吧:
1)为什么要针对接口编程,而不是针对实现编程呢?
很简单的一个例子,我们现在使用的电脑有各式的品牌,联想、神舟、戴尔等等,
电脑需要用到鼠标,键盘;假设鼠标、键盘是针对某一个品牌的机器实现去做的话,那么我们将会遇到什么问题呢?
那么我们市面上的键盘和鼠标就都是各式各样的,有一天鼠标,或键盘坏了,我们要怎么去买呢?难道记住这个电脑是什么品牌,什么型号,还有什么类型的去买么?这样会疯掉的。现在我们的电脑基本上都是使用USB接口的了,无论是键盘也好,鼠标也好,我们只要买USB接口的就可以使用了,同时,使用USB接口还可以有其他的扩展,只要实现了,这个接口,实现怎么样都没关系,例如,实现了USB接口的小台灯,只要接上USB线就可以照明了;又如实现了USB
接口的充电器,接到我们的电脑上就可以充电了。
2)高层模块不应该依赖低层模块,两者都应该依赖其抽象又如何理解呢?这个问题也可以这么问:为什么要叫倒置(倒转)呢?
在面向过程的开发中,为了使用常用的代码可以复用,一般都会把这些常用的代码写成许许多多函数的程序库,这样我们做新项目的时候,就去调用这些函数就可以了。
例如:我们做的项目大多要访问数据库,所以我们就把数据库的代码写成了函数,每次做新项目时就去调用这些函数,这也就是高层依赖于低层模块了。
问题就出现在这里了,我们在做新项目的时候,会发现业务逻辑的高层模块是一样的,我们希望能重用这些高层模块,但是这些高层模块和低层模块的数据库绑定在一起了,这样我们就没办法复用这些高层模块了。
如果我们的高层模块和低层模块都依赖于抽象,具体一点就是依赖于接口或抽象类,只要接口够稳定,那么任何一个的更改都不用担心其他受到影响了。
3)为什么依赖了抽象的接口或是抽象类,就不怕更改了呢?要解决这个问题,先看看里氏替换原则。
里氏替换原则:
一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别。也就是说,在软件里面,把父类都替换成它的子类,程序的行为没有变化;
简单的说:子类型必须能够替换掉它们的父类型。例如:企鹅在生物学分类上是属于鸟的,但是在编程中,企鹅就不能以父类的(鸟)的身份出现。
假设有一个鸟的父类,拥有一个会飞的方法fly(),我们是不能让企鹅继承于鸟的,这样当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正的被复用,
而子类也能够在父类的基础上添加新的行为。例如:猫是继承动物类的,以动物的身份拥有吃、喝、跑、叫等行为,
可当某一天,我们需要狗、牛、羊也拥有类似的行为,由于它们都是继承于动物,所以除了更改实例化的地方,程序其他地方就需要改变了。
正是由于子类型的可替换性才使得使用父类类型在的模块在无需修改的情况下就可以扩展。UML图:
高层模块依赖于接口或抽象类,低层模块实现接口或抽象类。依赖倒置原则其实就是谁也不依靠谁,除了约定的接口,这样大家都可以灵活自由了。