@H_301_1@依赖倒置原则来源:
类A直接依赖类B,假如要将类改为依赖类C,则必须通过修改类的代码来达成。这种场景下,类一般是高层模块,负责复杂的业务逻辑;类和类是低层模块,负责基本的原子操作;假如修改类,会给程序带来不必要的风险。
依赖倒置原则(Dependence Inversion Principle)是程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
A.高层模块不应该依赖低层模块,二者都应该依赖其抽象。抽象不应该依赖细节,细节应该依赖抽象。(细节:即具体的实现类,实现接口或者继承抽象类所产生的类,可以通过关键字new直接被实例化。)
B.即抽象类或接口,是不能够实例化的。
依赖倒置原则出发点:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。通过抽象(抽象类或接口)使各个类或模块的实现彼此独立,不相互影响,实现模块间的松耦合。使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。这也是开闭原则的基础。
@H_301_1@依赖倒置原则的三种实现方式:
(1).通过构造函数传递依赖对象;
比如在构造函数中的需要传递的参数是抽象类或接口的方式实现。
(2).通过setter方法传递依赖对象;
即在我们设置的set方法中的参数为抽象类或接口,来实现传递依赖对象。
(3).接口声明实现依赖对象,也叫接口注入;
即在函数声明中参数为抽象类或接口,来实现传递依赖对象,从而达到直接使用依赖对象的目的。
可总结为接口传递,构造方法传递和setter方法传递。依赖倒置原则的核心就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置;
@H_301_1@依赖倒置原则示例:
我们金融系统开发中,经常需要将k线数据或分时数据转存到其它文件或报表中,因此需要进行数据格式转换。在客户数据操作类中将调用数据格式转换类的方法实现格式转换和数据库插入操作,初始设计方案结构如图1所示:
在编码实现图1所示结构时,我们发现该设计方案存在一个非常严重的问题,由于每次转换数据时数据来源不一定相同,因此需要更换数据转换类,如有时候需要将KlineDataConvertor改为MinDataConvertor,此时,需要修改CustomerDAO的源代码,而且在引入并使用新的数据转换类时也不得不修改CustomerDAO的源代码,系统扩展性较差,违反了开闭原则,现需要对该方案进行重构。
由于CustomerDAO针对具体数据转换类编程,因此在增加新的数据转换类或者更换数据转换类时都不得不修改CustomerDAO的源代码。我们可以通过引入抽象数据转换类解决该问题,在引入抽象数据转换类DataConvertor之后,CustomerDAO针对抽象类DataConvertor编程,而将具体数据转换类名存储在配置文件中,符合依赖倒转原则。根据里氏代换原则,程序运行时,具体数据转换类对象将替换DataConvertor类型的对象,程序不会出现任何问题。更换具体数据转换类时无须修改源代码,只需要修改配置文件;如果需要增加新的具体数据转换类,只要将新增数据转换类作为DataConvertor的子类并修改下载文件结构即可,原有代码无须做任何修改,满足开闭原则。重构后的结构如图2所示:
在上述重构过程中,我们使用了开闭原则、里氏代换原则和依赖倒转原则,在大多数情况下,这三个设计原则会同时出现,开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段,它们相辅相成,相互补充,目标一致,只是分析问题时所站角度不同而已。
@H_301_1@依赖倒置原则使用总结:
(1).低层模块尽量都要有抽象类或接口,或者两者都有。
(2).变量的声明类型尽量是抽象类或接口。
(3).使用继承时遵循里氏替换原则。