享元模式就是运行共享技术有效地支持大量细粒度的对象,避免大量拥有相同内容的小类的开销,使大家共享一个类。
实现方式
- 将需要改写为享元的类成员变量拆分为两个部分:
- 内在状态:包含不变的、可在许多对象中重复使用的数据的成员变量
- 外在状态:包含每个对象各自不同的情景数据的成员变量
- 保留类中表示内在状态的成员变量,并将其属性设置为不可修改。这些变量仅可在构造函数中获得初始数值
找到所有使用外在状态成员变量的方法,为在方法中所用的每个成员变量新建一个参数,并使用该参数代替成员变量 ```typescript /**
- The Flyweight stores a common portion of the state (also called intrinsic
- state) that belongs to multiple real business entities. The Flyweight accepts
- the rest of the state (extrinsic state, unique for each entity) via its
method parameters. */ 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.
); } }
4. 你可以有选择地创建工厂类来管理享元缓存池, 它负责在新建享元时检查已有的享元。 如果选择使用工厂, 客户端就只能通过工厂来请求享元, 它们需要将享元的内在状态作为参数传递给工厂
```typescript
/**
* The Flyweight Factory creates and manages the Flyweight objects. It ensures
* that flyweights are shared correctly. When the client requests a flyweight,
* the factory either returns an existing instance or creates a new one, if it
* doesn't exist yet.
*/
class FlyweightFactory {
private flyweights: {[key: string]: Flyweight} = <any>{};
constructor(initialFlyweights: string[][]) {
for (const state of initialFlyweights) {
this.flyweights[this.getKey(state)] = new Flyweight(state);
}
}
/**
* Returns a Flyweight's string hash for a given state.
*/
private getKey(state: string[]): string {
return state.join('_');
}
/**
* Returns an existing Flyweight with a given state or creates a new one.
*/
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);
}
}
}
- 客户端必须存储和计算外在状态 (情景) 的数值, 因为只有这样才能调用享元对象的方法。 为了使用方便, 外在状态和引用享元的成员变量可以移动到单独的情景类中
```typescript
/**
- The client code usually creates a bunch of pre-populated flyweights in the
- initialization stage of the application. */ const factory = new FlyweightFactory([ [‘Chevrolet’, ‘Camaro2018’, ‘pink’], [‘Mercedes Benz’, ‘C300’, ‘black’], [‘Mercedes Benz’, ‘C500’, ‘red’], [‘BMW’, ‘M5’, ‘red’], [‘BMW’, ‘X6’, ‘white’], // … ]); factory.listFlyweights();
// …
function addCarToPoliceDatabase( ff: FlyweightFactory, plates: string, owner: string, brand: string, model: string, color: string, ) { console.log(‘\nClient: Adding a car to database.’); const flyweight = ff.getFlyweight([brand, model, color]);
// The client code either stores or calculates extrinsic state and passes it
// to the flyweight's methods.
flyweight.operation([plates, owner]);
}
addCarToPoliceDatabase(factory, ‘CL234IR’, ‘James Doe’, ‘BMW’, ‘M5’, ‘red’);
addCarToPoliceDatabase(factory, ‘CL234IR’, ‘James Doe’, ‘BMW’, ‘X1’, ‘red’);
factory.listFlyweights();
<a name="dIzSo"></a>
# 实际使用场景
未使用享元时的代码:<br />创建了一万个实例,但是他们的某些字段数据都相同,造成内存浪费
```typescript
class Iphone11 {
constructor(model: string, screen: number, memory: number, sn: number) { }
}
const phones = [];
for (let i = 0; i < 10000; i++) {
let memory = i % 2 == 0 ? 128 : 256;
phones.push(new Iphone11("iPhone11", 6.1, memory, i));
}
使用享元模式:
将大部分相同的数据抽离到享元类中:
/**
* 内部状态:model, screen, memory
* 外部状态:sn
*/
class IphoneFlyweight {
constructor(model: string, screen: number, memory: number) {}
}
定义一个对象来保存享元对象,并提供一个方法根据参数来获取享元对象
class FlyweightFactory {
private phonesMap: { [s: string]: IphoneFlyweight } = {};
public get(model: string, screen: number, memory: number): IphoneFlyweight {
const key = model + screen + memory;
if (!this.phonesMap[key]) {
this.phonesMap[key] = new IphoneFlyweight(model, screen, memory);
}
return this.phonesMap[key];
}
}
定义 Iphone 类
class Iphone {
constructor(flyweight: IphoneFlyweight, sn: number) { }
}
定义 IphoneFactory 类
在型号、屏幕和内存相同的情况下,会共享由 IphoneFlyweight 享元类创建的享元对象。
class IphoneFactory {
private static flyweightFactory: FlyweightFactory = new FlyweightFactory();
public getIphone(
model: string,
screen: number,
memory: number,
sn: number
) {
const flyweight: IphoneFlyweight = IphoneFactory.flyweightFactory.get(
model,
screen,
memory
);
return new Iphone(flyweight, sn);
}
}
function show(): void {
const iphoneFactory = new IphoneFactory();
const phones = [];
for (let i = 0; i < 10000; i++) {
let memory = i % 2 == 0 ? 128 : 256;
phones.push(iphoneFactory.getIphone("iPhone11", 6.1, memory, i));
}
console.log("Already created 10000 iPhone11");
}