12k
All articles

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

依赖倒置原则阐述了抽象层如何在 TypeScript、Python 和 Java 代码中解耦高层模块与低层模块。

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

依赖倒置原则(Dependency Inversion Principle,DIP)是面向对象设计的五大SOLID原则之一。它通过改变依赖方向——从具体实现到抽象契约,帮助创建灵活、解耦的系统。本文将用通俗易懂的语言帮助你理解DIP,并提供可立即应用的示例。

要点

  • 高层模块不应依赖低层模块,两者都应依赖抽象
  • DIP使架构更灵活且可测试
  • 你将看到如何在多种语言中应用DIP的实际示例

官方定义

高层模块不应依赖低层模块。两者都应依赖抽象。抽象不应依赖细节。细节应依赖抽象。

让我们分解一下:

  • 高层模块:包含业务逻辑(例如下订单)
  • 低层模块:处理特定任务(例如发送电子邮件)
  • 抽象:定义行为而非实现的接口或基类

视觉类比

想象你有一个OrderService,当订单生成时发送电子邮件通知。

不使用DIP:

OrderService --> EmailService

OrderServiceEmailService紧密耦合。你无法轻易替换或模拟它。

使用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");
  }
}

这使OrderServiceEmailService紧密耦合。

重构:使用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的一种技术。

为什么DIP使测试变得更容易?

因为你可以用遵循相同接口的模拟对象或存根替换真实依赖。

在JavaScript中应用DIP需要接口吗?

在TypeScript中接口很有帮助,但在JavaScript中你可以使用对象形状契约和模式来达到相同效果。

Listen to your bugs 🧘, with OpenReplay

See how users use your app and resolve issues fast.
Loved by thousands of developers

We use cookies to improve your experience. By using our site, you accept cookies.