JavaScript中的依赖注入:Node.js实用指南

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

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

开始课程

引言:代码紧耦合的问题



每个Node.js开发者迟早都会遇到这样的情况:一个模块的修改会引发一连串其他十几个模块的改动。在编写单元测试时,这个问题尤为突出:为了测试一个服务,不得不启动数据库、外部API和缓存。问题的根源在于紧耦合。



依赖注入(DI)是一种解决此问题的设计模式。类或函数不再自行创建其依赖项,而是从外部获取它们。这使得代码更加灵活、可测试,并符合SOLID原则中的依赖反转原则。



在本文中,我们将从实践角度剖析DI:从简单的手动注入到在Node.js中使用专业容器。



1. 手动依赖注入:第一步



糟糕的方法:紧耦合

考虑一个典型的发送电子邮件的服务示例:



// userService.jsconst emailService = require('./emailService');

class UserService { async registerUser(email) { // 对具体实现的紧耦合 await emailService.sendWelcomeEmail(email); console.log('用户已注册'); }}

module.exports = UserService;


问题:在测试UserService时,无法将emailService替换为模拟对象。必须使用像proxyquire或jest.mock这样的复杂工具。



好的方法:通过构造函数注入

重写服务,通过构造函数传递依赖项:



// userService.jsclass UserService {  constructor(emailService) {    this.emailService = emailService;  }

async registerUser(email) { await this.emailService.sendWelcomeEmail(email); console.log('用户已注册'); }}

module.exports = UserService;


现在我们可以轻松地测试该服务:



// userService.test.jsconst UserService = require('./userService');

const mockEmailService = { sendWelcomeEmail: jest.fn().mockResolvedValue(true)};

test('应该发送欢迎邮件', async () => { const service = new UserService(mockEmailService); await service.registerUser('test@test.com'); expect(mockEmailService.sendWelcomeEmail).toHaveBeenCalledWith('test@test.com');});


2. JavaScript中的依赖注入类型



在JavaScript/Node.js中,有三种主要的依赖注入方式:



2.1. 构造函数注入

最常用且最可预测的方式。依赖项在创建对象时传递:



class DatabaseService {  constructor(connection) {    this.connection = connection;  }

async query(sql) { return this.connection.execute(sql); }}

// 使用const db = new DatabaseService(new MySQLConnection());


2.2. Setter注入

适用于依赖项可以在对象运行过程中更改的情况:



class LoggerService {  setLogger(logger) {    this.logger = logger;  }

log(message) { this.logger.write(`[${new Date().toISOString()}] ${message}`); }}

const logger = new LoggerService();logger.setLogger(new ConsoleLogger());logger.log('应用程序已启动');


2.3. 函数参数注入

非常适合函数式方法和工具函数:



function sendNotification(user, emailService, smsService) {  emailService.send(user.email, '通知');  smsService.send(user.phone, '通知');}

// 测试const mockEmail = { send: jest.fn() };const mockSms = { send: jest.fn() };sendNotification({ email: 'a@b.com', phone: '123' }, mockEmail, mockSms);


3.

博客

书籍推荐