文章目录
一、定义
(1)高层模块不应该依赖低层模块,它们都应该依赖抽象;抽象不应该依赖于细节,细节应该依赖于抽象。无论是高层模块还是低层模块,都应该依赖于抽象。
另外一种表述:要针对接口编程,不要针对实现编程。实现指的是具体的类,是不稳定的。
(2)结构良好的面向对象架构都具有清晰的层次定义,每个层次通过一个定义良好的、受控的接口向外提供一组内聚的服务。
二、常见的系统模块设计
一般认为系统的结构是:高层的调用低层的,低层的再调用更低层的,如下图:
Policy Layer:决策层
Mechanism Layer:机制层
Utility Layer:工具层
这样看起来似乎是正确的,但是高层对低层的改动是敏感的。低层发生改变了,比如低层的方法名,参数改变。会导致中层发生改变,接着导致高层发生改变。
三、依赖导致原则分析
(1)简单来说,依赖倒置原则就是指:代码要依赖于抽象的类,而不要依赖于具体的类;要针对接口或抽象类编程,而不是针对具体类编程。
(2)更好的描述是:不要依赖那些容易变化的具体类。如果要继承一个类,从一个抽象类继承;如果要持有一个类的引用,从一个抽象类引用;如果要调用一个函数,从一个抽象的函数调用。
什么是依赖,只要 A类 和 B类 发生了关系,那么它们就存在了依赖关系。就比如说 A类调用了B类中的方法,那么就可以说A依赖于B。这里的依赖是泛指的。
(3)实现开闭原则的关键是抽象化,并且从抽象化导出具体化实现,如果说开闭原则是面向对象设计的目标,那么依赖倒置原则就是主要手段。
(4)依赖倒置原则的常用实现方式之一是在代码中使用抽象类,而将具体类放在配置文件中。
如下图:
(5)依赖倒置原则主要解决的是类之间的耦合关系,对类进行解耦。
类之间的耦合关系大致为:
1)零耦合:两个类是独立的,没有关系
2)具体耦合关系:两个类发生了关系并且都是具体的类
3)抽象耦合关系
依赖倒置原则要求客户端依赖于抽象耦合,以抽象方式耦合是依赖倒置原则的关键。
比如,服务端C 经常要调用服务端S 提供的服务,如果S是抽象的是允许的,如果是具体的类是不允许的(这种关系是不稳定的)。
四、依赖倒置原则修改系统结构
(1)之前的系统结构是高层的模块通过调用低层的模块,模块之间的相互调用来来完成系统的功能设计。但是这样的设计是存在问题的,低层发生改变,高层也得发生改变。
(2)用依赖倒置原则进行改进:之前的模块(类)之间的关系是具体的耦合关系,要改进的话在两个模块之间增加一个抽象的层,通常是增加抽象的接口。
这样高层依赖的是抽象的接口,低层去实现接口
五、依赖注入
依赖注入是为了更好地实现依赖倒置的配套技术。依赖注入是什么?
如下图,A依赖于抽象的C,B依赖于抽象C;而A需要的是具体的B,也就是说要把具体的B传给A,怎么把具体的B传给A呢?这就是涉及到依赖注入。
或者说,A依赖于抽象的接口C,在具体的运行中,要给A具体的类,具体的类如何传给A,用什么方式。
1.依赖注入的方式
(1)构造注入
通过构造函数注入实例变量。
(2)设值注入
通过 Setter 方法注入实例变量。这个方法最常用
(3)接口注入
通过接口方法注入实例变量。
六、依赖倒置原则实例
某系统提供一个数据转换模块,可以将来自不同数据源的数据转换成多种格
式,如可以转换来自数据库的数据(DatabaseSource)、也可以转换来自文本文件的数据(TextSource),转换后的格式可以是XML文件(XMLTransformer)、也可以是 XLS文件(XLSTransformer) 等。
1、存在问题
现在 MainClass 依赖的四个模块都是具体的
由于需求的变化,该系统可能需要增加新的数据源或者新的文件格式,每增加一个新的类型的数据源或者新的类型的文件格式,客户类MainClass都需要修改源代码,以便使用新的类,但违背了开闭原则。
2、依赖倒置原则重构
现使用依赖倒置原则对其进行重构,存在的问题就是 MainClass 依赖的四个模块都是具体的,具体的东西是不稳定的。解决的思路就是将具体的模块抽象化,让 MainClass 依赖的是抽象的东西。