为什么我必须在 JS 中使用依赖注入?它有什么用处?结论

2025-06-10

为什么我必须在 JS 中使用依赖注入?

这为什么有用?

结论

依赖注入

每当我们着手一个项目,无论是现有的还是新建的,我们总是会思考如何让项目更易于管理、更可扩展、更易于测试。这时,依赖注入就派上用场了。

但首先,我们所说的依赖注入是什么意思?

它是一种软件设计模式,通过将获取依赖项的责任移到依赖它的代码之外,我们可以使代码易于单元测试。它也指将代码中某些部分的依赖项提供给依赖这些依赖项的对象、函数或模块。

这为什么有用?

如前所述,如果我们抽象出代码片段,在需要时了解它们所需的具体依赖关系,就能使代码片段更容易测试。例如:

//File: services/notifications/index.js

import User from '../database/models/user';
import { logError } from './logger';
import { sendEmailNotification } from './emails';

const DEFAULT_NOTIFICATION_MESSAGE = 'Hi, friend. :)';

export const sendNotificationsToUsers = async (ids = []) => {
  try {
    const users = await User.find({
      id: ids
    });

    const promises = users.map(({
      email,
      // This we'll add notifications into a queue to process them in the background.
      // Don't freak out.
    }) => sendEmailNotification(email, DEFAULT_NOTIFICATION_MESSAGE));

    await Promise.all(promises);

    return {
      success: true
    };
  } catch (e) {
    logError(e);

    return {
      success: false
    };
  }
};
Enter fullscreen mode Exit fullscreen mode

在前面的例子中,我们尝试向某些用户发送通知。这没什么奇怪的。但是,为了测试这个功能,我们需要做什么呢?模拟这三个依赖项以便将其作为一个单元进行测试,是否容易?

对我来说,不是。

我该怎么办?

这里有两种情况。第一种,模块中只有这个函数需要依赖项。第二种,模块中的所有函数都需要这些依赖项。

对于第一种情况:

//File: services/notifications/index.js

const DEFAULT_NOTIFICATION_MESSAGE = 'Hi, friend. :)';

export const sendNotificationsToUsers = async ({
  User,
  logger,
  notifier
}, ids = []) => {
  try {
    const users = await User.find({
      id: ids
    });

    const promises = users.map((user => notifier.notifyUser(user, DEFAULT_NOTIFICATION_MESSAGE)));

    await Promise.all(promises);

    return {
      success: true
    };
  } catch (e) {
    logger.logError(e);

    return {
      success: false
    };
  }
};
Enter fullscreen mode Exit fullscreen mode

我们在这里做了一些重构:

  • 我们将依赖项作为函数中的第一个配置参数传递sendNotificationsToUsers
  • 我们允许函数不关心需要哪种类型的记录器或通知器,这样该函数就可以通用,并且可以在将来重复使用。比如使用短信通知,或者任何我们想到的。

现在这段代码是可测试的,并且可以模拟依赖项:

//some test file
import assert from 'assert';
import {
  sendNotificationsToUsers
}
from '../core/services/notifications';

describe('Notification service', () => {
  const mockUserDB = {
    find() {
      return Promise.resolve([{
        email: 'some-email@gmail.com',
        phone: 'some-phone-number'
      }]);
    }
  };
  const logger = {
    logError(e) {
      console.log(e);
    }
  }

  describe('#sendNotificationsToUsers', () => {
    it('can send notifications via emails', async () => {
      const notifier = {
        notifyUser(_user, _message) {
          return Promise.resolve(true);
        }
      };
      const notificationResponse = await sendNotificationsToUsers({
        User: mockUserDB,
        logger,
        notifier,
      }, [1]);

      assert(notificationResponse, 'Notifications failed to be sent.');
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

那么整个模块要求依赖关系怎么样?

我们只需将模块导出为接受这些依赖项的函数并按如下方式使用它:

export default ({
  User,
  logger,
  notifier
}) => ({
  async sendNotificationsToUsers(ids = []) {
    try {
      const users = await User.find({
        id: ids
      });

      const promises = users.map((user => notifier.notifyUser(user, DEFAULT_NOTIFICATION_MESSAGE)));

      await Promise.all(promises);

      return {
        success: true
      };
    } catch (e) {
      logger.logError(e);

      return {
        success: false
      };
    }
  }
});

//Usage

import User from 'services/users';
import logger from 'services/logger';
import notifier from 'services/emails';
import getNotificationsService from 'services/notifications';

const { sendNotificationsToUsers } = getNotificationsService({ User, logger, notifier });

sendNotificationsToUsers([1, 2, 3]);
Enter fullscreen mode Exit fullscreen mode

结论

我相信这种编码方式对我们所有人都有帮助,它将帮助我们将模块编写为真正的单元,也将帮助我们在测试和开发时提高工作效率。

Please share your thoughts, corrections or comments below and until the next time. Happy Coding.

鏂囩珷鏉ユ簮锛�https://dev.to/vyzaldysanchez/why-do-i-have-to-use-dependency-injection-in-js-code-35an
PREV
基本 Bash 自动化:用于组织我的下载文件夹的脚本
NEXT
基于 Quasar 和 Firebase 构建的简单生产力应用程序