依赖倒置是什么
依赖倒置原则规定:高层模块与低层模块都依赖抽象,抽象不依赖细节,细节依赖抽象。它约束的是依赖方向,而不是依赖如何传入——后者是依赖注入,属于实现手段。两者常被混为一谈,导致引入了注入容器却依然把框架类型散布在业务代码里。
落地标志
判断是否真的做到了依赖倒置,看业务核心包的 import 列表:不出现框架、数据库驱动与 HTTP 客户端类型。此时核心用例可以用内存实现直接跑单元测试,不需要启动容器与数据库,替换外部依赖也不必改动业务代码。
基本信息
| 与依赖注入的区别 | 依赖倒置是设计原则;依赖注入是把依赖从外部传入的实现方式 |
|---|---|
| 接口归属 | 接口定义在需要它的模块一侧,由外层适配器实现 |
| 收益 | 核心逻辑可独立测试,外部实现可替换 |
| 常见误用 | 按实现类逐方法生成接口,抽象没有业务含义 |
依赖方向示例
依赖方向示例
# 业务核心模块:定义接口,不引入任何框架类型
interface OrderRepository { Order findById(OrderId id); }
class PlaceOrderService {
private final OrderRepository repo; # 构造注入
PlaceOrderService(OrderRepository repo) { this.repo = repo; }
}
# 外层适配器模块:实现接口,依赖方向指向业务核心
class MysqlOrderRepository implements OrderRepository { ... }检查与演练
- 检查业务包 import:不出现框架、驱动与 HTTP 客户端类型
- 替换演练:注入内存实现跑单测,验证核心逻辑与外层解耦
- 依赖方向:适配器依赖核心,核心不反向依赖适配器
- 接口粒度:按业务用例命名,而不是按数据表或框架方法命名
常见误用
把依赖注入框架当作依赖倒置的完成标志,是最常见的误用:容器帮你装配实现,但业务代码里仍然到处出现具体框架类型时,依赖方向没有改变。另一种误用是给每个类都配一个接口,接口与实现一比一对应,抽象没有承担任何业务含义,只增加了阅读与重构成本。
把接口定义在需要它的模块一侧(向内),由外层适配器实现,抽象就不会跟着外部技术变化。这也是端口与适配器结构的核心约定。