在一个复杂的软件架构里,为了方便软件的开发和维护,会把软件划分为多个
模块
,并进行
分层
。
如下图所示,理想情况下:
(1)
模块B
调用操作系统的接口,实现自己的功能,并提供
API
给模块A;
(2)
模块A
调用模块B的
API
,实现自己的功能,并提供
API
给用户界面;
(3)
用户界面
调用模块A的
API
,实现自己的功能,并提供
API
给客户;
上面所说的“调用”是一种
依赖
关系。
A
需要调用
B
提供
API
才能完成自己的功能,称为
A
依赖
B
。
上层
软件和
下层
软件是相互的,比如
A
是
B
的上层,
A
是
用户界面
的下层。
正向依赖
正向依赖指上层模块依赖下层模块,只是最普遍的依赖关系。
用户界面依赖
A
、
B
和操作系统。
A
依赖
B
和操作系统。
B
依赖操作系统。
双向依赖
在某些情况下,
下层模块也要依赖上层模块
,形成了双向依赖。
如图所示,在
A
依赖
B
的同时,
B
也依赖了
A
。
双向依赖会引发一系列问题,如编译bug、维护性降低。
出现双向依赖意味着软件的设计有问题,需要对相关模块进行更细致的分层,或使用依赖倒置解决。
依赖倒置
依赖倒置提供了一种
下层依赖上层
手段。
如下图所示,当
B
需要调用到
A
的接口时,不直接调用它(会产生双向依赖),而是调用
B
自己定义的一个
虚接口
,在运行时由
A
提供这个
虚接口的实现
。
在
C
语言中,使用
函数指针
表示这个虚接口,运行时把
B
依赖的
A
的接口赋值给这个函数指针,
B
通过函数指针间接调用
A
的接口。
在
C++
语言中,除了函数指针,还可以使用面向对象语言专有的
虚函数
,由
A
实现或重写
B
定义的虚函数。
所谓“
倒置
”是指:上层模块从依赖方变成了被依赖方;下层模块从被依赖方变成了依赖方。就是
反向
依赖的意思。