应用Douglas Crockford组合模式时如何实现状态共享?
如何在Crockford组合模式中实现状态共享并保留私有性与不可变性?
这确实是Douglas Crockford这套组合模式里的一个经典矛盾点——咱们既想让组合的多个构造函数共享同一份状态,又不想破坏私有状态的封装性,还要维持实例的不可变性。其实核心解法就是把共享状态抽离成独立的闭包容器,让所有需要共享的构造函数都依赖这个容器,而不是各自维护私有副本。
具体实现步骤
1. 创建共享状态容器(复用性优先)
先把需要共享的私有状态封装到一个独立的工厂函数里,这个容器只暴露操作状态的方法,不直接对外暴露变量:
const createSharedState = (initialSpec) => { // 共享的私有状态,仅在容器内部可见 let { a } = initialSpec; // 只提供受控的状态操作方法,供内部构造函数调用 return { updateA(newValue) { a = newValue; }, getCurrentA() { return a; } }; };
2. 改造原有构造函数,依赖共享容器
原来的constructor_x和constructor_x2不再自己维护a,而是接收共享状态容器作为参数,通过容器的方法操作状态:
// 改造constructor_x const constructor_x = (sharedState) => { const method_x = () => { sharedState.updateA('Updated by constructor_x'); }; return Object.freeze({ method_x }); }; // 改造constructor_x2 const constructor_x2 = (sharedState) => { const method_z = () => { sharedState.updateA('Updated by constructor_x2'); }; return Object.freeze({ method_z }); };
3. 组合构造函数,统一管理共享状态
在顶层的组合构造函数constructor_y中,创建共享状态容器,再把它传递给所有需要共享状态的子构造函数,同时保留自己的私有状态:
const constructor_y = (spec) => { // 1. 创建共享状态容器,传入初始值 const sharedState = createSharedState(spec); // 2. 仅属于constructor_y的私有状态 let { b } = spec; // 3. 把共享容器传给子构造函数 const { method_x } = constructor_x(sharedState); const { method_z } = constructor_x2(sharedState); // 4. 定义constructor_y自己的方法 const method_y = () => { b = 'Updated by constructor_y'; }; const checkStates = () => { console.log('Shared a:', sharedState.getCurrentA()); console.log('Private b:', b); }; // 5. 冻结并返回公共接口,完全不暴露共享容器 return Object.freeze({ method_x, method_y, method_z, checkStates }); };
方案优势
- 严格的私有性:共享状态
a只存在于createSharedState的闭包中,实例上没有任何可直接访问的私有变量(完全避免了Crockford吐槽的下划线前缀);constructor_y的b依然是独立的局部变量,不会泄露。 - 真正的状态共享:
constructor_x和constructor_x2操作的是同一个a,因为它们拿到的是同一个共享容器的引用。 - 实例不可变性:所有返回的公共接口都用
Object.freeze冻结,无法修改或添加方法,保证了实例的稳定性。 - 灵活性:子构造函数(比如
constructor_x)依然可以独立使用——只要给它传入一个符合接口的共享容器即可,不会像暴露get/set那样限制其独立可用性。
简化版(适合简单场景)
如果共享状态不需要在多个不同的组合场景中复用,也可以直接在顶层构造函数里创建共享闭包,省去单独的容器工厂:
const constructor_y = (spec) => { // 共享状态直接在顶层闭包中定义 let { a } = spec; // 私有状态b let { b } = spec; // 定义共享状态的操作方法 const sharedOps = { setA(val) { a = val; }, getA() { return a; } }; // 传递给子构造函数 const { method_x } = constructor_x(sharedOps); const { method_z } = constructor_x2(sharedOps); // ... 其他方法 return Object.freeze({ method_x, method_z, method_y }); };
核心思路总结
本质上就是把共享的私有状态从单个构造函数的闭包提升到一个更高层级的闭包容器中,让所有需要访问该状态的构造函数都通过这个容器来操作状态,而不是各自维护副本。这样既保证了状态的封装性,又实现了共享,同时还能维持实例的不可变性。
内容的提问来源于stack exchange,提问作者Zeljko Brckovic
相关产品推荐
相关产品推荐

