Dependence Inversion Principle
依赖倒置原则(Dependence Inversion Principle)
如果开闭原则是实现面向对象设计的目标,那么依赖倒置原则是实现面向对象设计的主要实现机制。
What 什么是依赖倒置原则?
高层模块不应该依赖于底层模块,他们都应该依赖与抽象,抽象不应该依赖于细节,细节应该依赖于抽象
简单来说,依赖倒置原则,是针对抽象进行编程,而不是针对具体实现进行编程。
Why 为什么使用依赖倒置原则?
依赖倒置原则针对抽象层进行编程,在引入抽象层之后,系统的灵活性将会大大增加。在程序中尽量使用抽象层进行编程,而将具体的实现子类写在配置文件中,当系统行为发生变化的时候,只需要对抽象类进行扩展,并且修改配置文件,无需修改系统的原有代码,在不修改的情况下扩展系统的功能,符合开闭原则。
How 如何使用依赖倒置原则?
在实现依赖倒置原则是,需要针对抽象层进行编程,能将具体的类通过依赖注入的方式注入到其他对象中,依赖注入指的是当一个对象需要与其他对象发送依赖关系的时候采用抽象的形式来注入说依赖的对象。
常用的有三种依赖注入的方式,分别是构造注入、setter(设值)注入、通过接口注入。通过构造函数进行注入是通过构造函数传入具体类的对象。设值注入指的是通过setter方法传入具体类的对象。接口注入是通过在接口中声明的业务方法,来传入具体的对象。这些方法在定义的时候使用的是抽象类型,在运行的时候传入具体的对象,由具体的子类来覆盖父类。所以从这儿可以看出,依赖倒置原则在使用的过程中通常会使用到历史替换原则。
示例
某公司开发人员在开发CRM系统时发现给系统经常需要将存储在TXT或者Excel文件中的客户信息转存到数据库中。因此需要进行数据格式的转换。在客户数据操作类CUstomerDAO中将调用数据给转换类的方法来实现格式转换,初始设计的类图接口如图所示:
但是在实现类图的时候,该软件公司开发人员发现该设计方案存在一个非常严重的问题,由于每次转换数据是数据来源不一定相同,因此需要经常更换数据转换类,例如有时候需要将TXTDataConvertor改为ExcelDataConvertor,这个时候就需要修改CustomerDAO的源代码,而且如果引入并使用新的数据转换类也不得不修改CustomerDAO额源代码,系统扩展性差,违背了开闭原则,现在需要对该方案进行重构。
在本例中,由于CustomerDAO针对具体数据转换类进行编程,因此在增加新的数据转换类或者更换数据转换类时不得不修改CustomerDAO的源代码。这个使用我们可以引入抽象数据转换类来解决该问题。引入抽象数据转换类DataConvertor之后,CustomerDAO针对抽象类DataConvertor进行编程,而将具体的数据转换类类名存储在配置文件中,符合依赖倒置原则。根据里氏替换原则,程序在运行时具体数据转换类对象将替换DataConvertor类型的对象,程序不会产生任何异常。
在更换具体的数据转换类时无需修改源代码,只需要修改配置文件;如果需要增加新的具体数据转换类,只要将新增数据转换类作为DataConvertor的子类并修改配置文件。原有代码无需做任何修改,满足开闭原则。重构之后的示意图:
结论
在上述重构过程中同时使用了开闭原则,里氏替换原则和依赖倒置原则,在大多数情况下这3个原则会同时出现,开闭原则是目标,里氏替换原则是基础,依赖倒置原则是手段,他们相辅相成,相互补充,目标一致,只是分析问题时所占的角度不同而已。