关键词:结构型 享元 FlyWeight 性能优化
什么是享元模式?
享元的本质是通过 共享基础类 来处理大量相同的对象。主要是用于性能优化。
先看一个简单案例对享元模式有个概念,
需求:现在需要做50串烤肉,50串烤玉米。一般情况下需要准备100根签子串烤串。
但是今天店里只有两根签子,所以我们只能两根签子这样一批批上。
这就是享元模式,拿时间换内存。
// 签子足够的时候 class Skewers { constructor(type, skewerIdx) { this.type = type; this.skewerIdx = skewerIdx; } showInfo() { console.log(`第${this.skewerIdx}根${this.type}签`); } } for (let i = 0; i < 50; i++) { const m = new Skewers("肉", i + 1); m.showInfo(); } for (let i = 0; i < 50; i++) { const f = new Skewers("蔬菜", i + 1); f.showInfo(); } // 场景优化后 class Skewers { constructor(type) { this.type = type; } showInfo() { console.log(`第${this.skewerIdx}根${this.type}签`); } } const m = new Skewers("肉"); const f = new Skewers("蔬菜"); for (let i = 0; i < 50; i++) { f.skewerIdx = i + 1; m.skewerIdx = i + 1; f.showInfo(); m.showInfo(); }
应用场景
判断是否需要使用享元模式:
- 项目中存在大量重复对象
- 重复的对象为不可变对象,或者一些属性是不可变的。
总而言之需要达到:复用对象,节省内存 的作用。
前端的话,比如:地图上的 cluster 和 mark 都是常用的场景,和绘图相关的都有可能使用到。
如何实现享元模式?
前提:享元对象是不可变对象。
核心:在内存中只保留一份实例,供多处代码引用。
- 将需要改写为享元类属性拆分为两个部分,
- 内在状态:包含不变的、可在许多对象中重用的部分。
- 外在状态:包含每个对象各自不同的情景数据的成员变量
- 保留类中表示内在状态的属性, 并设置为不可修改。这些属性的值只能在
constructor
中获取。 - 找到所有使用外在状态成员变量的方法,为方法中所用的每个成员变量新建一个参数, 并使用该参数代替成员变量。
- 创建工厂类来管理享元缓存池,它负责在新建享元时检查已有的享元。
// 享元模式 class Flyweight { // 内部状态 private sharedState: any; constructor(sharedState: any) { this.sharedState = sharedState; } // 传入外部状态 public operation(uniqueState): void { const s = JSON.stringify(this.sharedState); const u = JSON.stringify(uniqueState); console.log(`Flyweight: Displaying shared (${s}) and unique (${u}) state.`); } } // 工厂方法-生成多个实例 class FlyweightFactory { private flyweights: { [key: string]: Flyweight } = <any>{}; constructor(initialFlyweights: string[][]) { for (const state of initialFlyweights) { this.flyweights[this.getKey(state)] = new Flyweight(state); } } private getKey(state: string[]): string { return state.join("_"); } public getFlyweight(sharedState: string[]): Flyweight { const key = this.getKey(sharedState); if (!(key in this.flyweights)) { console.log( "FlyweightFactory: Can't find a flyweight, creating new one." ); this.flyweights[key] = new Flyweight(sharedState); } else { console.log("FlyweightFactory: Reusing existing flyweight."); } return this.flyweights[key]; } public listFlyweights(): void { const count = Object.keys(this.flyweights).length; console.log(`\nFlyweightFactory: I have ${count} flyweights:`); for (const key in this.flyweights) { console.log(key); } } } // 找到所有使用外在状态成员变量的方法,为在方法中所用的每个成员变量新建一个参数, 并使用该参数代替成员变量。 function addCarToPoliceDatabase( ff: FlyweightFactory, plates: string, owner: string, brand: string, model: string, color: string ) { const flyweight = ff.getFlyweight([brand, model, color]); flyweight.operation([plates, owner]); } const factory = new FlyweightFactory([ ["Chevrolet", "Camaro2018", "pink"], ["Mercedes Benz", "C300", "black"], ["Mercedes Benz", "C500", "red"], ["BMW", "M5", "red"], ["BMW", "X6", "white"], ]); factory.listFlyweights(); addCarToPoliceDatabase(factory, "CL234IR", "James Doe", "BMW", "M5", "red"); addCarToPoliceDatabase(factory, "CL234IR", "James Doe", "BMW", "X1", "red"); factory.listFlyweights();
参考资料
《JavaScript 设计模式与开发实践》