依赖倒置原则要求高层模块和低层模块都依赖抽象,抽象不依赖细节;Java中通过接口或抽象类定义抽象,由外部注入具体实现来解耦,如UserService依赖UserRepository接口而非其实现类。

依赖倒置(Dependency Inversion Principle,DIP)是面向对象设计中SOLID五大原则之一,核心思想是高层模块不应依赖低层模块,二者都应依赖抽象;抽象不应依赖细节,细节应依赖抽象。在Java中,它不是语法特性,而是一种设计指导思想,通过接口、抽象类等抽象机制来解耦模块间的依赖关系。
为什么需要依赖倒置?
不遵守依赖倒置时,常见写法是高层类直接new低层类实例:
❌ 反例(紧耦合):class UserService {
private MySQLUserRepository repository = new MySQLUserRepository(); // 直接依赖具体实现
}这样会导致:数据库换为Redis或MongoDB时,必须修改UserService;单元测试难以Mock;可维护性和可扩展性差。
如何用Java实现依赖倒置?
关键在于将“依赖具体实现”改为“依赖抽象”,再通过外部注入具体实现:
立即学习“Java免费学习笔记(深入)”;
- 定义接口(抽象)描述行为,如
UserRepository - 让低层模块实现该接口(如
MySQLUserRepository、RedisUserRepository) - 高层模块(如
UserService)只持有接口类型字段,并通过构造器/Setter注入具体实现 - 依赖关系由外部(如Spring容器、工厂类或测试代码)控制,而非在类内部硬编码
✅ 正例(符合DIP):
interface UserRepository {
User findById(Long id);
}
class MySQLUserRepository implements UserRepository { / ... / }
class RedisUserRepository implements UserRepository { / ... / }
class UserService {
private final UserRepository repository; // 依赖抽象,不关心谁实现
public UserService(UserRepository repository) { // 构造注入
this.repository = repository;
}}
依赖倒置 ≠ 依赖注入,但常配合使用
依赖倒置是设计原则,强调“谁依赖谁”的方向;依赖注入(DI)是实现手段,解决“怎么把依赖给进来”的问题。Spring框架的@Autowired、构造器注入等,都是落实依赖倒置的常用方式。但即使不用Spring,手动传入实现类(比如测试时传入Mock对象),也已体现DIP思想。
常见误区提醒
- 误以为“用了接口就自动满足DIP”——若高层仍自己new实现类,只是形式上用了接口,本质仍是依赖细节
- 过度抽象:不是每个类都要抽接口,只对可能变化或需替换的部分(如数据源、支付渠道、通知方式)抽象
- 混淆依赖倒置与控制反转(IoC):IoC是更广的概念,DI是IoC的一种,DIP是指导IoC/DI如何设计的准则










