目录
当前架构分析
依赖关系现状
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class TreemapHeat { constructor(data, option) { this.dataManager = new DataManager(option); this.renderEngine = new RenderEngine(option, new ResourceManager()); this.interactionHandler = new InteractionHandler(option); this.stateManager = new StateManager();
this.renderEngine.resourceManager = this.resourceManager; this.interactionHandler.container = this.renderEngine.getContainer(); } }
|
存在的问题
- 紧耦合:管理器之间直接依赖
- 测试困难:难以模拟依赖进行单元测试
- 扩展性差:添加新功能需要修改现有代码
- 配置分散:配置项在多个管理器间重复
依赖图
1 2 3
| DataManager → RenderEngine → InteractionHandler ↓ ↓ ↓ StateManager ← ResourceManager ← EventSystem
|
IoC容器适用性评估
适合引入IoC的理由
1. 复杂的管理器网络
- 多个管理器之间存在复杂的依赖关系
- 需要统一管理这些依赖关系
2. 多配置来源
- 用户传入的option
- 默认配置
- 环境特定配置
- 动态运行时配置
3. 扩展性需求
- 不同的渲染引擎(ZRender/PixiJS/Canvas)
- 多种布局算法
- 可插拔的交互处理器
- 自定义数据处理器
4. 测试需求
1 2 3 4 5 6 7 8 9 10 11 12 13
| it('should handle zoom events', () => { const handler = new InteractionHandler(config); });
it('should handle zoom events', () => { const mockRenderEngine = mock<RenderEngine>(); const mockStateManager = mock<StateManager>(); const handler = container.resolve<InteractionHandler>(); });
|
具体实现方案
方案一:轻量级DI容器(推荐)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| class TreemapContainer { constructor() { this.services = new Map(); this.singletons = new Map(); this.factories = new Map(); }
registerSingleton(key, factory) { this.factories.set(key, factory); return this; }
registerInstance(key, instance) { this.services.set(key, instance); return this; }
resolve(key) { if (this.services.has(key)) { return this.services.get(key); }
if (this.singletons.has(key)) { return this.singletons.get(key); }
if (this.factories.has(key)) { const instance = this.factories.get(key)(this); this.singletons.set(key, instance); return instance; }
throw new Error(`Service ${key} not found`); }
createChild() { const child = new TreemapContainer(); child.services = new Map(this.services); child.factories = new Map(this.factories); return child; } }
const container = new TreemapContainer() .registerSingleton('config', () => merge(defaultConfig, userConfig)) .registerSingleton('stateManager', (c) => new StateManager(c.resolve('config'))) .registerSingleton('resourceManager', () => new ResourceManager()) .registerSingleton('renderEngine', (c) => new RenderEngine( c.resolve('config'), c.resolve('resourceManager') )) .registerSingleton('dataManager', (c) => new DataManager(c.resolve('config'))) .registerSingleton('interactionHandler', (c) => new InteractionHandler(c.resolve('config')));
class TreemapHeat { constructor(data, option, container = null) { this.container = container || this.createDefaultContainer(option); this.data = data;
this.stateManager = this.container.resolve('stateManager'); this.renderEngine = this.container.resolve('renderEngine'); this.dataManager = this.container.resolve('dataManager'); this.interactionHandler = this.container.resolve('interactionHandler');
this.initialize(); }
createDefaultContainer(option) { return new TreemapContainer() .registerInstance('config', merge(defaultConfig, option)) .registerSingleton('stateManager', (c) => new StateManager(c.resolve('config'))) } }
|
方案二:Inversify.js完整实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| const TYPES = { Config: Symbol.for('Config'), StateManager: Symbol.for('StateManager'), RenderEngine: Symbol.for('RenderEngine'), DataManager: Symbol.for('DataManager'), InteractionHandler: Symbol.for('InteractionHandler'), ResourceManager: Symbol.for('ResourceManager') };
interface IConfig { width: number; height: number; scale: [number, number]; }
interface IStateManager { getViewState(): ViewState; updateViewState(updates: Partial<ViewState>): void; }
@injectable() class StateManager implements IStateManager { private viewState: ViewState;
constructor( @inject(TYPES.Config) private config: IConfig ) { this.viewState = { scale: config.scale[0], position: [0, 0] }; }
getViewState(): ViewState { return { ...this.viewState }; }
updateViewState(updates: Partial<ViewState>): void { } }
const container = new Container(); container.bind<IConfig>(TYPES.Config).toConstantValue(config); container.bind<IStateManager>(TYPES.StateManager).to(StateManager).inSingletonScope(); container.bind<IRenderEngine>(TYPES.RenderEngine).to(RenderEngine).inSingletonScope(); container.bind<IDataManager>(TYPES.DataManager).to(DataManager).inSingletonScope(); container.bind<IInteractionHandler>(TYPES.InteractionHandler).to(InteractionHandler).inSingletonScope();
@injectable() class TreemapHeat { constructor( @inject(TYPES.StateManager) private stateManager: IStateManager, @inject(TYPES.RenderEngine) private renderEngine: IRenderEngine, @inject(TYPES.DataManager) private dataManager: IDataManager, @inject(TYPES.InteractionHandler) private interactionHandler: IInteractionHandler ) {} }
|
方案三:函数式DI(最小化)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| const createTreemapServices = (config) => { const createResourceManager = () => new ResourceManager();
const createStateManager = () => new StateManager(config);
const createDataManager = () => new DataManager(config);
const createRenderEngine = (resourceManager) => new RenderEngine(config, resourceManager);
const createInteractionHandler = () => new InteractionHandler(config);
const resourceManager = createResourceManager(); const stateManager = createStateManager(); const dataManager = createDataManager(); const renderEngine = createRenderEngine(resourceManager); const interactionHandler = createInteractionHandler();
return { config, resourceManager, stateManager, dataManager, renderEngine, interactionHandler }; };
class TreemapHeat { constructor(data, option) { const services = createTreemapServices(option); Object.assign(this, services); this.data = data; this.initialize(); } }
|
小白教程:IoC带来的好处
没有IoC时的问题(现在的代码)
比喻:手工制作咖啡
1 2 3 4 5 6 7 8 9 10 11 12
| class TreemapHeat { constructor() { this.dataManager = new DataManager(option); this.renderEngine = new RenderEngine(option); this.interactionHandler = new InteractionHandler(option);
this.renderEngine.dataManager = this.dataManager; } }
|
问题:
- 想换咖啡豆? 要改代码
new DataManager()
- 想测试? 无法提供假咖啡豆
- 想加糖? 要在多个地方改代码
- 依赖混乱:谁依赖谁,看不出来
有IoC后的好处(方案一)
比喻:咖啡店点单系统
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| class TreemapContainer { constructor() { this.services = new Map(); }
registerSingleton(key, factory) { this.factories.set(key, factory); }
resolve(key) { return this.factories.get(key)(this); } }
const container = new TreemapContainer() .registerSingleton('dataManager', () => new DataManager(option)) .registerSingleton('renderEngine', () => new RenderEngine(option));
class TreemapHeat { constructor(container) { this.dataManager = container.resolve('dataManager'); this.renderEngine = container.resolve('renderEngine'); } }
|
IoC如何解决四个核心问题
1. 如何解决架构分裂?
现在的问题:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class TreemapHeat { constructor() { this.dataManager = new DataManager(); } }
class DataManager { constructor(config) { } }
|
IoC解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13
| const container = new TreemapContainer() .registerSingleton('dataManager', () => { return useNewArchitecture ? new NewDataManager() : new OldDataManager(); });
class TreemapHeat { constructor(container) { this.dataManager = container.resolve('dataManager'); } }
|
通俗理解: 就像餐厅菜单,不管厨房怎么换(新旧架构),顾客点的都是”美式咖啡”,不用关心怎么做。
2. 如何提升可测试性?
现在的问题:
1 2 3 4 5 6
| it('should render correctly', () => { const treemap = new TreemapHeat(data, option); });
|
IoC解决方案:
1 2 3 4 5 6 7 8 9
| it('should render correctly', () => { const testContainer = new TreemapContainer() .registerSingleton('dataManager', () => new FakeDataManager());
const treemap = new TreemapHeat(testContainer); });
|
通俗理解: 就像电影拍摄,可以用假道具测试,不用每次都用真品(节省时间,避免意外)。
3. 如何增强扩展性?
现在的问题:
1 2 3 4 5 6 7 8 9 10
| class TreemapHeat { constructor() { this.dataManager = new DataManager();
} }
|
IoC解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class AnalyticsPlugin { apply(container) { container.registerSingleton('analytics', () => new AnalyticsService()); } }
const container = new TreemapContainer(); new AnalyticsPlugin().apply(container); new LoggingPlugin().apply(container);
const treemap = new TreemapHeat(container);
|
通俗理解: 就像手机,不用重装系统就能装新App,想用就装,不用就卸载。
4. 如何改善维护性?
现在的问题:
1 2 3 4 5 6 7 8
| class TreemapHeat { constructor() { this.dataManager = new DataManager(); this.renderEngine = new RenderEngine(this.dataManager); this.interactionHandler = new InteractionHandler(); } }
|
IoC解决方案:
1 2 3 4 5 6 7 8
| const container = new TreemapContainer() .registerSingleton('config', () => config) .registerSingleton('dataManager', () => new DataManager()) .registerSingleton('renderEngine', () => new RenderEngine()) .registerSingleton('interactionHandler', () => new InteractionHandler());
|
通俗理解: 就像乐高说明书,所有零件和步骤都列出来,不用猜要用什么。
推荐方案
考虑因素
包体积考虑
- 当前打包后 ~100KB,Inversify.js 会增加 50% 体积
- 轻量级容器 < 5KB,影响可忽略
学习成本
- 团队成员无需学习新的装饰器语法
- 保持现有的编程范式
渐进式迁移
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class TreemapHeat { constructor(data, option) { if (option.container) { this.container = option.container; } else { this.container = this.createDefaultContainer(option); }
this.services = { stateManager: this.container.resolve('stateManager'), renderEngine: this.container.resolve('renderEngine'), dataManager: this.container.resolve('dataManager'), interactionHandler: this.container.resolve('interactionHandler') }; } }
|
扩展性示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class TreemapPlugin { constructor(name, setup) { this.name = name; this.setup = setup; }
apply(container) { this.setup(container); } }
const container = new TreemapContainer(); const pixiPlugin = new TreemapPlugin('pixi-renderer', (container) => { container.registerSingleton('renderEngine', (c) => new PixiRenderEngine(c.resolve('config'))); });
const analyticsPlugin = new TreemapPlugin('analytics', (container) => { container.registerSingleton('analytics', (c) => new AnalyticsService(c.resolve('stateManager'))); });
[pixiPlugin, analyticsPlugin].forEach(plugin => plugin.apply(container));
|
最终推荐:方案一(轻量级DI容器)
核心理由:
- 解决架构分裂:统一新旧架构的依赖管理
- 提升可测试性:可以轻松模拟依赖进行单元测试
- 增强扩展性:支持插件系统和运行时替换组件
- 改善维护性:依赖关系清晰,降低耦合度
- 包体积小(< 5KB),对库类项目友好
- 学习成本低,渐进式迁移
总结
是否适合引入IoC:强烈推荐 ✅
核心价值:
- 解决架构分裂:统一新旧架构的依赖管理
- 提升可测试性:可以轻松模拟依赖进行单元测试
- 增强扩展性:支持插件系统和运行时替换组件
- 改善维护性:依赖关系清晰,降低耦合度
建议方案:
- 首选:自定义轻量级DI容器(< 5KB)
- 备选:函数式DI模式(< 1KB)
- 不推荐:Inversify.js(包体积太大)
实施步骤:
- 先实现轻量级容器
- 逐步迁移管理器到容器管理
- 支持插件扩展机制
- 完善测试覆盖
简单理解:
- 没有IoC:像手工作坊,每样东西都要自己做
- 有IoC:像现代化工厂,标准化零件,即插即用
这样的重构将显著提升组件的架构质量和可维护性!
IoC vs 代理模式
常见误解
很多人容易混淆IoC和代理模式,认为IoC就是代理的概念。实际上它们是解决不同问题的不同模式。
代理模式(Proxy Pattern)
代理是结构型设计模式,核心是控制访问:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class ImageProxy { constructor(realImage) { this.realImage = realImage; }
display() { console.log('加载中...'); this.realImage.display(); console.log('加载完成'); } }
const realImage = new RealImage(); const proxy = new ImageProxy(realImage); proxy.display();
|
代理的特点:
- 代理和原对象实现相同接口
- 代理包装原对象
- 主要用于:延迟加载、访问控制、缓存、日志等
IoC容器(控制反转)
IoC是架构原则,核心是依赖管理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Container { constructor() { this.services = new Map(); }
register(key, factory) { this.services.set(key, factory); }
resolve(key) { return this.services.get(key)(this); } }
const container = new Container(); container.register('database', () => new Database()); container.register('userService', (c) => new UserService(c.resolve('database')));
const userService = container.resolve('userService');
|
IoC的特点:
- 容器管理对象生命周期
- 处理依赖注入
- 关注整体架构,而非单个对象
关键区别对比
| 特征 |
代理模式 |
IoC容器 |
| 目的 |
控制对单个对象的访问 |
管理整个应用的依赖关系 |
| 范围 |
对象级别 |
应用级别 |
| 关系 |
1:1(一个代理包装一个对象) |
1:N(容器管理多个对象) |
| 主要功能 |
访问控制、延迟加载 |
依赖注入、生命周期管理 |
| 使用场景 |
需要拦截方法调用时 |
需要解耦依赖时 |
实际例子对比
代理模式的典型应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class ApiProxy { constructor(realApi) { this.realApi = realApi; this.cache = new Map(); }
async fetchData(url) { if (this.cache.has(url)) { return this.cache.get(url); } const data = await this.realApi.fetchData(url); this.cache.set(url, data); return data; } }
class UserProxy { constructor(realUser) { this.realUser = realUser; }
deletePost(postId) { if (this.realUser.isAdmin) { return this.realUser.deletePost(postId); } throw new Error('无权限删除'); } }
|
IoC容器的典型应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class UserService { constructor(database, logger) { this.database = database; this.logger = logger; } }
class AppContainer { constructor() { this.services = new Map(); this.singletons = new Map(); }
resolve(key) { if (!this.singletons.has(key)) { this.singletons.set(key, this.services.get(key)(this)); } return this.singletons.get(key); } }
|
它们的关系
IoC容器内部可能会使用代理模式,但这是两个不同的概念:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class AdvancedContainer { createProxy(service, key) { return new Proxy(service, { get(target, prop) { if (prop === 'dispose') { return () => this.disposeService(key); } return target[prop]; } }); }
disposeService(key) { console.log(`Disposing service: ${key}`); } }
|
简单总结
代理模式 = 门卫
- 只管一扇门的进出
- 可以检查证件、记录日志
- 但是不管整个小区的管理
IoC容器 = 物业管理公司
- 管理整个小区的所有服务
- 负责服务之间的协调
- 处理服务的创建和销毁
所以,IoC不是代理的概念,它们是解决不同问题的不同模式:
在刚才的Treemap组件中,我们用IoC是为了解决多个管理器之间的依赖关系混乱问题,而不是为了控制对某个对象的访问。
DI和IoC的关系和区别
核心关系
DI(依赖注入)是IoC(控制反转)的一种实现方式
可以这样理解:
- IoC 是一种设计原则/理念
- DI 是一种具体的实现技术
比喻说明
IoC(控制反转)= 转让控制权
- 就像雇佣司机:你不再自己开车(反转控制)
- 但具体怎么实现?可以有多种方式
DI(依赖注入)= 司机自己拿钥匙
- 司机主动获取钥匙(依赖注入)
- 这是一种实现”不自己开车”的具体方式
具体区别
IoC(控制反转)
定义:将对象创建和依赖关系的管理从代码内部转移到外部容器
核心思想:好莱坞原则 - “Don’t call us, we’ll call you”
1 2 3 4 5 6 7 8 9 10 11 12 13
| class UserService { constructor() { this.database = new Database(); } }
class UserService { constructor(database) { this.database = database; } }
|
DI(依赖注入)
定义:一种实现IoC的具体技术,通过外部注入依赖而不是内部创建
三种注入方式:
1. 构造函数注入(推荐)
1 2 3 4 5 6 7 8 9
| class UserService { constructor(database, logger) { this.database = database; this.logger = logger; } }
const userService = new UserService(database, logger);
|
2. 属性注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class UserService { setDatabase(database) { this.database = database; }
setLogger(logger) { this.logger = logger; } }
const userService = new UserService(); userService.setDatabase(database); userService.setLogger(logger);
|
3. 接口注入(较少使用)
1 2 3 4 5 6 7 8 9 10
| interface Injectable { inject(dependencies: object): void; }
class UserService implements Injectable { inject({ database, logger }) { this.database = database; this.logger = logger; } }
|
更完整的例子
没有使用IoC/DI
1 2 3 4 5 6 7 8 9 10 11 12
| class OrderService { constructor() { this.database = new MySQLDatabase(); this.logger = new FileLogger(); this.payment = new AlipayService(); }
createOrder(orderData) { } }
|
使用DI(实现了IoC)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class OrderService { constructor(database, logger, payment) { this.database = database; this.logger = logger; this.payment = payment; }
createOrder(orderData) { } }
const orderService1 = new OrderService( new MySQLDatabase(), new FileLogger(), new AlipayService() );
const orderService2 = new OrderService( new PostgreSQLDatabase(), new ConsoleLogger(), new WechatPayService() );
|
使用DI容器(更完整的IoC实现)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| class DIContainer { constructor() { this.services = new Map(); }
register(key, factory) { this.services.set(key, factory); }
resolve(key) { const factory = this.services.get(key); return factory(this); } }
const container = new DIContainer(); container.register('database', () => new MySQLDatabase()); container.register('logger', () => new FileLogger()); container.register('payment', () => new AlipayService()); container.register('orderService', (c) => new OrderService( c.resolve('database'), c.resolve('logger'), c.resolve('payment') ) );
const orderService = container.resolve('orderService');
|
关系总结
概念层级
1 2 3 4 5
| IoC(控制反转)- 设计原则 ↓ DI(依赖注入)- 实现方式 ↓ DI容器 - 工具实现
|
其他IoC实现方式
除了DI,IoC还有其他实现方式:
1. 服务定位器(Service Locator)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class ServiceLocator { static getDatabase() { return this.database; }
static getLogger() { return this.logger; } }
class UserService { constructor() { this.database = ServiceLocator.getDatabase(); this.logger = ServiceLocator.getLogger(); } }
|
2. 事件驱动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class EventEmitter { constructor() { this.events = {}; }
on(event, callback) { if (!this.events[event]) { this.events[event] = []; } this.events[event].push(callback); }
emit(event, data) { if (this.events[event]) { this.events[event].forEach(callback => callback(data)); } } }
const emitter = new EventEmitter(); emitter.on('user.created', (user) => { console.log('User created:', user); });
emitter.emit('user.created', { id: 1, name: 'John' });
|
实际应用中的选择
何时使用DI
- 需要解耦组件依赖
- 要求良好的可测试性
- 项目规模较大,依赖关系复杂
何时使用Service Locator
- 老旧系统改造
- 某些特定场景(如插件系统)
- 不推荐在新项目中使用
何时使用事件驱动
简单总结
IoC = 哲学思想
DI = 具体技术
关系:
- IoC是”做什么”(What)
- DI是”怎么做”(How)
- DI是实现IoC最流行的方式
就像:
- 健康生活是IoC(原则)
- 健身是DI(实现方式之一)
- 还有其他实现方式如饮食控制、规律作息等
IoC vs 中介者模式
核心区别
IoC(控制反转) 是一种架构原则,关注的是依赖管理和控制权转移。
中介者模式 是一种行为型设计模式,关注的是对象间通信和解耦。
中介者模式的特点
中介者模式的核心是集中管理对象间的交互:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| class ChatRoomMediator { constructor() { this.users = []; }
register(user) { this.users.push(user); user.setMediator(this); }
send(message, fromUser) { this.users.forEach(user => { if (user !== fromUser) { user.receive(message); } }); } }
class User { constructor(name) { this.name = name; this.mediator = null; }
setMediator(mediator) { this.mediator = mediator; }
send(message) { this.mediator.send(message, this); }
receive(message) { console.log(`${this.name} received: ${message}`); } }
const mediator = new ChatRoomMediator(); const user1 = new User('Alice'); const user2 = new User('Bob');
mediator.register(user1); mediator.register(user2);
user1.send('Hello Bob');
|
IoC容器的特点
IoC容器关注的是依赖注入和生命周期管理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| class Container { constructor() { this.services = new Map(); }
register(key, factory) { this.services.set(key, factory); }
resolve(key) { return this.services.get(key)(this); } }
class ServiceA { constructor(serviceB) { this.serviceB = serviceB; } }
class ServiceB { constructor() {} }
const container = new Container(); container.register('serviceB', () => new ServiceB()); container.register('serviceA', (c) => new ServiceA(c.resolve('serviceB')));
const serviceA = container.resolve('serviceA');
|
关键区别对比
| 特征 |
IoC容器 |
中介者模式 |
| 目的 |
管理依赖注入和对象生命周期 |
管理对象间的通信和交互 |
| 关注点 |
对象创建和依赖关系 |
对象间的消息传递 |
| 关系 |
1:N(容器管理多个服务) |
N:N(多个对象通过中介者通信) |
| 通信方式 |
不直接处理对象间通信 |
专门处理对象间通信 |
| 实现时机 |
对象创建时 |
对象运行时 |
| 典型应用 |
依赖注入、服务定位 |
聊天室、事件系统、UI组件协调 |
实际应用场景
中介者模式的典型应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| class ChatMediator { }
class FormMediator { constructor() { this.fields = []; }
registerField(field) { this.fields.push(field); field.on('change', () => this.validateForm()); }
validateForm() { const isValid = this.fields.every(field => field.isValid()); this.emit('formValidation', isValid); } }
class TrafficMediator { constructor() { this.lights = []; }
registerLight(light) { this.lights.push(light); }
changeLight(changedLight) { this.lights.forEach(light => { if (light !== changedLight) { light协调状态(changedLight); } }); } }
|
IoC容器的典型应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class UserService { constructor(database, cache, logger) { this.database = database; this.cache = cache; this.logger = logger; } }
class PluginManager { constructor(container) { this.container = container; this.plugins = []; }
loadPlugin(pluginClass) { const plugin = new pluginClass(this.container); this.plugins.push(plugin); } }
class ConfigManager { constructor(container) { this.config = container.resolve('config'); } }
|
混合使用的情况
在实际项目中,IoC容器和中介者模式经常结合使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| class Container { constructor() { this.services = new Map(); }
}
class EventBus { constructor() { this.handlers = new Map(); }
on(event, handler) { if (!this.handlers.has(event)) { this.handlers.set(event, []); } this.handlers.get(event).push(handler); }
emit(event, data) { if (this.handlers.has(event)) { this.handlers.get(event).forEach(handler => handler(data)); } } }
const container = new Container(); container.register('eventBus', () => new EventBus()); container.register('serviceA', (c) => { const service = new ServiceA(); service.eventBus = c.resolve('eventBus'); return service; });
class ServiceA { constructor() { this.eventBus = null; }
doSomething() { this.eventBus.emit('serviceA.action', { data: 'something' }); } }
|
在Treemap组件中的应用
在我们的Treemap组件中:
IoC容器的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class TreemapContainer { constructor() { this.services = new Map(); }
register(key, factory) { this.services.set(key, factory); }
resolve(key) { return this.services.get(key)(this); } }
const container = new TreemapContainer() .register('stateManager', () => new StateManager()) .register('renderEngine', () => new RenderEngine()) .register('dataManager', () => new DataManager());
|
中介者模式的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| class ServiceMediator { constructor() { this.services = new Map(); this.eventBus = new EventBus(); }
registerService(name, service) { this.services.set(name, service); service.setMediator(this); }
notify(service, event, data) { this.services.forEach((otherService, name) => { if (otherService !== service) { otherService.handleEvent(event, data); } }); } }
const mediator = new ServiceMediator(); mediator.registerService('renderEngine', renderEngine); mediator.registerService('interactionHandler', interactionHandler);
interactionHandler.on('zoom', (data) => { mediator.notify(interactionHandler, 'zoom', data); });
|
总结
IoC容器不是中介者模式,它们解决不同的问题:
IoC容器 = 对象工厂 + 依赖管理器
- 关注:对象如何被创建和组装
- 解决:依赖注入和生命周期管理
- 时机:对象创建阶段
中介者模式 = 通信协调器
- 关注:对象间如何通信
- 解决:对象间的复杂交互
- 时机:对象运行阶段
在实际项目中,它们经常配合使用:
- IoC容器负责创建和管理对象
- 中介者模式负责对象间的通信协调
就像:
- IoC容器 = 汽车工厂(制造汽车)
- 中介者模式 = 交通信号灯(协调汽车行驶)