享元模式就是运行共享技术有效地支持大量细粒度的对象,避免大量拥有相同内容的小类的开销,使大家共享一个类。

实现方式

  1. 将需要改写为享元的类成员变量拆分为两个部分:
    1. 内在状态:包含不变的、可在许多对象中重复使用的数据的成员变量
    2. 外在状态:包含每个对象各自不同的情景数据的成员变量
  2. 保留类中表示内在状态的成员变量,并将其属性设置为不可修改。这些变量仅可在构造函数中获得初始数值
  3. 找到所有使用外在状态成员变量的方法,为在方法中所用的每个成员变量新建一个参数,并使用该参数代替成员变量 ```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.); } }

  1. 4. 你可以有选择地创建工厂类来管理享元缓存池, 它负责在新建享元时检查已有的享元。 如果选择使用工厂, 客户端就只能通过工厂来请求享元, 它们需要将享元的内在状态作为参数传递给工厂
  2. ```typescript
  3. /**
  4. * The Flyweight Factory creates and manages the Flyweight objects. It ensures
  5. * that flyweights are shared correctly. When the client requests a flyweight,
  6. * the factory either returns an existing instance or creates a new one, if it
  7. * doesn't exist yet.
  8. */
  9. class FlyweightFactory {
  10. private flyweights: {[key: string]: Flyweight} = <any>{};
  11. constructor(initialFlyweights: string[][]) {
  12. for (const state of initialFlyweights) {
  13. this.flyweights[this.getKey(state)] = new Flyweight(state);
  14. }
  15. }
  16. /**
  17. * Returns a Flyweight's string hash for a given state.
  18. */
  19. private getKey(state: string[]): string {
  20. return state.join('_');
  21. }
  22. /**
  23. * Returns an existing Flyweight with a given state or creates a new one.
  24. */
  25. public getFlyweight(sharedState: string[]): Flyweight {
  26. const key = this.getKey(sharedState);
  27. if (!(key in this.flyweights)) {
  28. console.log('FlyweightFactory: Can\'t find a flyweight, creating new one.');
  29. this.flyweights[key] = new Flyweight(sharedState);
  30. } else {
  31. console.log('FlyweightFactory: Reusing existing flyweight.');
  32. }
  33. return this.flyweights[key];
  34. }
  35. public listFlyweights(): void {
  36. const count = Object.keys(this.flyweights).length;
  37. console.log(`\nFlyweightFactory: I have ${count} flyweights:`);
  38. for (const key in this.flyweights) {
  39. console.log(key);
  40. }
  41. }
  42. }
  1. 客户端必须存储和计算外在状态 (情景) 的数值, 因为只有这样才能调用享元对象的方法。 为了使用方便, 外在状态和引用享元的成员变量可以移动到单独的情景类中 ```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]);

  1. // The client code either stores or calculates extrinsic state and passes it
  2. // to the flyweight's methods.
  3. flyweight.operation([plates, owner]);

}

addCarToPoliceDatabase(factory, ‘CL234IR’, ‘James Doe’, ‘BMW’, ‘M5’, ‘red’);

addCarToPoliceDatabase(factory, ‘CL234IR’, ‘James Doe’, ‘BMW’, ‘X1’, ‘red’);

factory.listFlyweights();

  1. <a name="dIzSo"></a>
  2. # 实际使用场景
  3. 未使用享元时的代码:<br />创建了一万个实例,但是他们的某些字段数据都相同,造成内存浪费
  4. ```typescript
  5. class Iphone11 {
  6. constructor(model: string, screen: number, memory: number, sn: number) { }
  7. }
  8. const phones = [];
  9. for (let i = 0; i < 10000; i++) {
  10. let memory = i % 2 == 0 ? 128 : 256;
  11. phones.push(new Iphone11("iPhone11", 6.1, memory, i));
  12. }

使用享元模式:
将大部分相同的数据抽离到享元类中:

  1. /**
  2. * 内部状态:model, screen, memory
  3. * 外部状态:sn
  4. */
  5. class IphoneFlyweight {
  6. constructor(model: string, screen: number, memory: number) {}
  7. }

定义一个对象来保存享元对象,并提供一个方法根据参数来获取享元对象

  1. class FlyweightFactory {
  2. private phonesMap: { [s: string]: IphoneFlyweight } = {};
  3. public get(model: string, screen: number, memory: number): IphoneFlyweight {
  4. const key = model + screen + memory;
  5. if (!this.phonesMap[key]) {
  6. this.phonesMap[key] = new IphoneFlyweight(model, screen, memory);
  7. }
  8. return this.phonesMap[key];
  9. }
  10. }

定义 Iphone 类

  1. class Iphone {
  2. constructor(flyweight: IphoneFlyweight, sn: number) { }
  3. }

定义 IphoneFactory 类
在型号、屏幕和内存相同的情况下,会共享由 IphoneFlyweight 享元类创建的享元对象。

  1. class IphoneFactory {
  2. private static flyweightFactory: FlyweightFactory = new FlyweightFactory();
  3. public getIphone(
  4. model: string,
  5. screen: number,
  6. memory: number,
  7. sn: number
  8. ) {
  9. const flyweight: IphoneFlyweight = IphoneFactory.flyweightFactory.get(
  10. model,
  11. screen,
  12. memory
  13. );
  14. return new Iphone(flyweight, sn);
  15. }
  16. }
  1. function show(): void {
  2. const iphoneFactory = new IphoneFactory();
  3. const phones = [];
  4. for (let i = 0; i < 10000; i++) {
  5. let memory = i % 2 == 0 ? 128 : 256;
  6. phones.push(iphoneFactory.getIphone("iPhone11", 6.1, memory, i));
  7. }
  8. console.log("Already created 10000 iPhone11");
  9. }

参考资料

  1. 享元模式
  2. TypeScript 设计模式之享元模式