JavaScript中的依赖注入:初学者完整指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:什么是依赖注入,为什么需要它?

\n\n

想象一下你在组装一台电脑。与其购买一台现成的一体机,你分别选择 CPU、显卡、内存,然后将它们连接到主板上。如果你想升级显卡,你不需要更换整台电脑——只需更换一个组件即可。依赖注入 (DI) 在编程中的工作原理与此相同。

\n\n

依赖注入是一种设计模式,其中对象从外部获取其依赖项,而不是自己创建它们。在 JavaScript 和 Node.js 的上下文中,这意味着类或函数不会在内部创建其他类的实例,而是通过构造函数或参数接收它们。

\n\n

这种方法是 SOLID 中 依赖反转原则 的一部分,它使代码更加灵活、可测试和可维护。在本文中,我们将通过面向初学者的实际示例来讲解 DI。

\n\n

问题:代码的紧耦合

\n\n

考虑 Node.js 应用程序中的典型情况。假设我们有一个用于发送电子邮件通知的服务:

\n\n
// 没有依赖注入\nclass EmailService {\n  sendEmail(to, subject, body) {\n    console.log(`发送邮件至 ${to}: ${subject}`);\n    // 实际的发送逻辑\n  }\n}\n\nclass UserService {\n  constructor() {\n    this.emailService = new EmailService(); // ❌ 紧耦合\n  }\n\n  registerUser(email) {\n    // 用户注册逻辑\n    this.emailService.sendEmail(email, '欢迎!', '...');\n  }\n}\n\nconst userService = new UserService();\nuserService.registerUser('user@example.com');\n
\n\n

问题出在哪里?UserService 类与 EmailService 的具体实现紧密耦合。如果你想:

\n
    \n
  • 将发送邮件替换为发送短信
  • \n
  • 添加所有已发送消息的日志记录
  • \n
  • 在不实际发送邮件的情况下测试 UserService
  • \n
\n\n

你将不得不重写 UserService 的代码。这违反了开闭原则。

\n\n

解决方案:通过构造函数注入依赖

\n\n

第一种也是最简单的依赖注入方法是通过类的构造函数传递它们。让我们重写我们的示例:

\n\n
// 使用依赖注入\nclass EmailService {\n  sendEmail(to, subject, body) {\n    console.log(`发送邮件至 ${to}: ${subject}`);\n  }\n}\n\nclass SmsService {\n  sendSms(phone, message) {\n    console.log(`发送短信至 ${phone}: ${message}`);\n  }\n}\n\nclass UserService {\n  constructor(notificationService) {\n    this.notificationService = notificationService; // ✅ 依赖注入\n  }\n\n  registerUser(contact, isPhone = false) {\n    // 注册逻辑\n    if (isPhone) {\n      this.notificationService.sendSms(contact, '欢迎!');\n    } else {\n      this.notificationService.sendEmail(contact, '欢迎!', '...');\n    }\n  }\n}\n\n// 使用\nconst emailService = new EmailService();\nconst userServiceWithEmail = new UserService(emailService);\nuserServiceWithEmail.registerUser('user@example.com');\n\nconst smsService = new SmsService();\nconst userServiceWithSms = new UserService(smsService);\nuserServiceWithSms.registerUser('+1234567890', true);\n
\n\n

现在 UserService 不知道具体使用的是哪个服务——邮件还是短信。它只是调用传入对象的 sendEmail 或 sendSms 方法。这被称为依赖反转:高层模块不依赖于低层模块,两者都依赖于抽象。

\n\n

这种方法的优点:

\n
    \n
  • 灵活性:易于替换

博客

书籍推荐