依赖注入与控制反转
依赖注入与控制反转是构建松耦合系统的核心设计思想,广泛用于复杂前端架构。
核心概念
控制反转(IoC)
控制权从程序内部转移到外部容器,对象不再自行创建依赖,而是由外部提供。
依赖注入(DI)
IoC的具体实现方式,依赖对象通过构造函数、属性或接口从外部注入。
JavaScript
传统方式:对象内部创建依赖
IoC方式:外部容器注入依赖
实现方式
构造函数注入
JavaScript
// 不使用DI:紧耦合
class UserService {
constructor() {
this.db = new MySQLDatabase(); // 直接创建依赖
this.logger = new ConsoleLogger();
}
}
// 使用DI:松耦合
class UserService {
constructor(db, logger) {
this.db = db; // 依赖从外部注入
this.logger = logger;
}
}
// 注入依赖
const db = new MySQLDatabase();
const logger = new ConsoleLogger();
const userService = new UserService(db, logger);
工厂注入
JavaScript
class ServiceFactory {
constructor() {
this.db = null;
this.logger = null;
}
setDatabase(db) {
this.db = db;
}
createUserService() {
return new UserService(this.db, this.logger);
}
}
容器注入
JavaScript
// 简易IoC容器
class Container {
constructor() {
this.services = new Map();
this.instances = new Map();
}
// 注册服务
register(name, factory, dependencies = []) {
this.services.set(name, { factory, dependencies });
}
// 解析服务
resolve(name) {
if (this.instances.has(name)) {
return this.instances.get(name);
}
const service = this.services.get(name);
if (!service) {
throw new Error(`Service ${name} not found`);
}
const deps = service.dependencies.map(dep => this.resolve(dep));
const instance = service.factory(...deps);
this.instances.set(name, instance);
return instance;
}
}
// 使用容器
const container = new Container();
container.register('db', () => new MySQLDatabase());
container.register('logger', () => new ConsoleLogger());
container.register('userService', (db, logger) => new UserService(db, logger), ['db', 'logger']);
const userService = container.resolve('userService');
实际应用
与测试结合
JavaScript
// 生产环境
const realDb = new MySQLDatabase();
const realLogger = new ConsoleLogger();
const userService = new UserService(realDb, realLogger);
// 测试环境:轻松替换依赖
const mockDb = { find: jest.fn(), save: jest.fn() };
const mockLogger = { log: jest.fn() };
const testUserService = new UserService(mockDb, mockLogger);
前端框架中的DI
JavaScript
// Angular风格
@Injectable({ providedIn: 'root' })
class AuthService {
constructor(private http: HttpClient, private storage: StorageService) {}
}
// Vue/React中手动DI
class App {
constructor() {
const api = new ApiService();
const store = new Store();
this.authService = new AuthService(api, store);
}
}
IoC与DI的关系
| 概念 | 说明 | 角色 |
|---|---|---|
| IoC | 设计原则 | 控制权的转移 |
| DI | 实现模式 | IoC的具体实现 |
| IoC容器 | 工具框架 | 管理依赖注入 |
DI是IoC的一种实现方式,IoC还可以通过服务定位器、模板方法等模式实现。
最佳实践
text
// 1. 面向接口编程
class UserService {
constructor(database) { // 依赖抽象,非具体实现
this.database = database;
}
}
// 2. 使用依赖标记
const DEPENDENCIES = {
DATABASE: 'Database',
LOGGER: 'Logger',
CACHE: 'Cache'
};
// 3. 生命周期管理
class Container {
registerSingleton(name, factory) { /* 单例 */ }
registerTransient(name, factory) { /* 每次新建 */ }
registerScoped(name, factory) { /* 作用域内单例 */ }
}
要点总结
- IoC是原则:控制权从内部转移到外部
- DI是实现:通过构造函数、属性、接口注入依赖
- 核心价值:松耦合、易测试、易维护
- 容器作用:集中管理服务注册与依赖解析
- 测试友好:轻松注入Mock对象进行单元测试
存放路径:articles/JS/专家/设计模式与架构思想/依赖注入与控制反转.md
📝 发现内容有误?点击此处直接编辑