什么是依赖倒置原则?简单解释

依赖倒置原则(Dependency Inversion Principle,DIP)是面向对象设计的五大SOLID原则之一。它通过改变依赖方向——从具体实现到抽象契约,帮助创建灵活、解耦的系统。本文将用通俗易懂的语言帮助你理解DIP,并提供可立即应用的示例。
要点
- 高层模块不应依赖低层模块,两者都应依赖抽象
- DIP使架构更灵活且可测试
- 你将看到如何在多种语言中应用DIP的实际示例
官方定义
高层模块不应依赖低层模块。两者都应依赖抽象。抽象不应依赖细节。细节应依赖抽象。
让我们分解一下:
- 高层模块:包含业务逻辑(例如下订单)
- 低层模块:处理特定任务(例如发送电子邮件)
- 抽象:定义行为而非实现的接口或基类
视觉类比
想象你有一个OrderService
,当订单生成时发送电子邮件通知。
不使用DIP:
OrderService --> EmailService
OrderService
与EmailService
紧密耦合。你无法轻易替换或模拟它。
使用DIP:
OrderService --> INotificationService <-- EmailService
现在两个模块都依赖于一个抽象(INotificationService
)。
代码示例:不使用DIP(TypeScript)
class EmailService {
send(message: string) {
console.log(`Sending email: ${message}`);
}
}
class OrderService {
constructor(private emailService: EmailService) {}
placeOrder() {
this.emailService.send("Order placed");
}
}
这使OrderService
与EmailService
紧密耦合。
重构:使用DIP(TypeScript)
interface INotificationService {
send(message: string): void;
}
class EmailService implements INotificationService {
send(message: string) {
console.log(`Sending email: ${message}`);
}
}
class OrderService {
constructor(private notifier: INotificationService) {}
placeOrder() {
this.notifier.send("Order placed");
}
}
Python中的DIP
from abc import ABC, abstractmethod
class NotificationService(ABC):
@abstractmethod
def send(self, message: str):
pass
class EmailService(NotificationService):
def send(self, message: str):
print(f"Sending email: {message}")
class OrderService:
def __init__(self, notifier: NotificationService):
self.notifier = notifier
def place_order(self):
self.notifier.send("Order placed")
# 使用方式
service = OrderService(EmailService())
service.place_order()
Java中的DIP
interface NotificationService {
void send(String message);
}
class EmailService implements NotificationService {
public void send(String message) {
System.out.println("Sending email: " + message);
}
}
class OrderService {
private NotificationService notifier;
public OrderService(NotificationService notifier) {
this.notifier = notifier;
}
public void placeOrder() {
notifier.send("Order placed");
}
}
// 使用方式
OrderService service = new OrderService(new EmailService());
service.placeOrder();
为什么DIP很重要
- 可测试性:可以用模拟对象或假对象替换真实依赖
- 灵活性:无需修改高层逻辑即可切换实现
- 关注点分离:每个模块只做一项工作,通过契约进行通信
常见误解:DIP ≠ 依赖注入
它们相关但不相同:
- DIP是关于谁依赖于谁(依赖方向)
- 依赖注入是应用DIP的一种方式——通过注入依赖而非硬编码它们
何时使用DIP
在以下情况使用:
- 你想编写不关心底层实现的业务逻辑
- 你正在开发分层或模块化应用
- 你正在构建可测试或可扩展的系统
结论
依赖倒置原则是关于颠倒通常的依赖方向——使抽象而非实现定义你的架构。它使你的代码更可重用、可测试,并且对变化更具弹性。
常见问题
这是一种设计原则,高层模块和低层模块都依赖于抽象,而不是相互依赖。
不是。DIP是一个原则。依赖注入是实现DIP的一种技术。
因为你可以用遵循相同接口的模拟对象或存根替换真实依赖。
在TypeScript中接口很有帮助,但在JavaScript中你可以使用对象形状契约和模式来达到相同效果。
Listen to your bugs 🧘, with OpenReplay
See how users use your app and resolve issues fast. Loved by thousands of developers