Dependency Injection (DI) 是软件开发中的一种设计模式,旨在解耦代码和提高可维护性。核心思想是:对象的依赖关系由外部系统(如框架或容器)动态注入,而非由对象自身直接创建和管理其依赖
一、为什么使用DI
-
解耦组件间依赖
- 消除了对象与依赖项之间的紧耦合,使得组件更独立、可复用。
- 例如,Service A 依赖 DatabaseClient,不通过 new DatabaseClient() 硬编码创建,而是接收注入的实例。
-
提高可测试性
- 通过注入模拟对象(Mock/Stub),更容易编写单元测试。
- 例如测试时注入一个模拟的数据库客户端,避免依赖真实数据库。
-
集中化管理依赖
- 依赖关系集中在容器或框架中配置,修改依赖的实现只需改动配置,无需修改业务代码。
-
生命周期控制
- 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');