关键词:结构型 享元 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 设计模式与开发实践》