浅析DI

137 阅读2分钟

Dependency Injection (DI)  是软件开发中的一种设计模式,旨在解耦代码和提高可维护性。核心思想是:对象的依赖关系由外部系统(如框架或容器)动态注入,而非由对象自身直接创建和管理其依赖

一、为什么使用DI

  1. 解耦组件间依赖

    • 消除了对象与依赖项之间的紧耦合,使得组件更独立、可复用。
    • 例如,Service A 依赖 DatabaseClient,不通过 new DatabaseClient() 硬编码创建,而是接收注入的实例。
  2. 提高可测试性

    • 通过注入模拟对象(Mock/Stub),更容易编写单元测试。
    • 例如测试时注入一个模拟的数据库客户端,避免依赖真实数据库。
  3. 集中化管理依赖

    • 依赖关系集中在容器或框架中配置,修改依赖的实现只需改动配置,无需修改业务代码。
  4. 生命周期控制

    • DI 容器通常支持管理对象的生命周期(如单例、瞬态实例等)。

二、技术选型

TypeScript 优先选 Inversify 或 TypeDI,纯 JavaScript 可考虑 Awilix。

InversifyJS

  • 适用场景:TypeScript 项目(强类型依赖注入)。

  • 特点

    • 基于装饰器和接口,支持依赖的自动解析。
    • 使用容器绑定依赖关系,灵活且功能强大。
  • 代码示例

import { injectable, inject, Container } from 'inversify';
​
@injectable()
  class Logger {
    log(message: string) { console.log(message); }
  }
​
@injectable()
  class App {
    constructor(@inject(Logger) private logger: Logger) {}
  }
​
const container = new Container();
container.bind(Logger).toSelf();
container.bind(App).toSelf();
​
const app = container.get(App);
app.logger.log('Hello DI');

Awilix

  • 适用场景:Node.js 后端或通用 JavaScript 项目。

  • 特点

    • 轻量级,支持依赖解析和作用域(如请求级容器)。
    • 提供多种解析模式(如构造函数注入、解析函数)。
  • 代码示例

const { createContainer, asClass } = require('awilix');
​
class Logger {
  log(message) { console.log(message); }
}
​
class App {
  constructor({ logger }) {
    this.logger = logger;
  }
}
​
const container = createContainer();
container.register({
  logger: asClass(Logger),
  app: asClass(App),
});
​
const app = container.resolve('app');
app.logger.log('Hello DI');

TypeDI

  • 适用场景:TypeScript 项目,需要简单语法。

  • 特点

    • 支持构造函数注入、属性注入。
    • 与 TypeScript 类型系统深度集成。
  • 代码示例

import { Container, Service, Inject } from 'typedi';
​
@Service()
  class Logger {
    log(message: string) { console.log(message); }
  }
​
@Service()
  class App {
    constructor(@Inject() private logger: Logger) {}
  }
​
const app = Container.get(App);
app.logger.log('Hello DI');