知识卡片

依赖反转:面向对象编程对架构师的核心本质

结构图卡

内容

在[[多态本质是函数指针的安全化催生了插件式架构]]带来安全多态之前,源代码依赖不可避免地要跟随程序控制流:main函数调高层函数、高层调中层、中层调底层,每个调用方都必须用#include/import/using引用被调用方所在模块,控制流方向决定源代码依赖方向,软件架构师在这方面别无选择。一旦引入多态,情况彻底改变:假设模块HL1需要调用ML1模块中的F()函数,这个调用可以经由一个源代码级别的接口I来实现——运行时HL1依然会调用ML1里的F(),但ML1在源代码层面对接口I的依赖方向,恰好和实际的控制流方向相反,这就是依赖反转。这不是一个孤立的语法技巧,而是面向对象编程对架构师而言的核心本质:无论调用树里源代码依赖关系原本如何,架构师都可以借助接口把它反转过来,从而完全掌控系统里的源代码依赖关系,不再受控制流方向的束缚。它的实际用途是让数据库模块和用户界面模块都反过来依赖业务逻辑模块(而非业务逻辑依赖它们)——业务逻辑组件因此不需要在源代码里引入UI和数据库这两个模块,三者可以被编译成三个独立的部署单元(jar/DLL/Gem),业务逻辑可以独立于UI和数据库部署,修改UI或数据库不会影响业务逻辑;如果系统里所有组件都能这样独立部署,就意味着它们能被不同团队并行开发——独立部署能力与独立开发能力,正是依赖反转带来的现实收益。

结构图

flowchart LR
    subgraph 无多态: 依赖跟随控制流
    Main1[main] -->|控制流+源码依赖同方向| High1[高层函数]
    High1 -->|控制流+源码依赖同方向| Mid1[中层函数]
    end
    subgraph 有多态: 依赖反转
    HL1[高层模块HL1] -->|控制流| ML1[中层模块ML1]
    HL1 -.源码依赖.-> I[接口I]
    ML1 -.源码依赖: 方向与控制流相反.-> I
    end
    UI[用户界面] -.依赖.-> BL[业务逻辑]
    DB[数据库] -.依赖.-> BL
    BL -.不依赖UI和数据库,可独立部署独立开发.-> BL

参考来源

- 位置:《架构整洁之道》第5章《面向对象编程》"依赖反转""本章小结"(源文件:_epub-src/text/part0011_split_003.html) - 结论依据:原文用HL1调用ML1中F()函数经接口I实现、ML1对接口I的源码依赖方向与控制流方向相反的例子定义依赖反转,并说明这让业务逻辑组件可以不依赖UI和数据库、三者独立部署独立开发,本章小结明确将OOP定义为"以多态为手段来对源代码中的依赖关系进行控制的能力",直接支撑本卡片的结构图与解释。 - 原始内容:模块ML1和接口I在源代码上的依赖关系……该关系的方向和控制流正好是相反的,我们称之为依赖反转……面向对象编程就是以多态为手段来对源代码中的依赖关系进行控制的能力。