享元模式

50 阅读4分钟

Flyweight

别名:缓存、Cache

定义

通过共享 多个对象所共有的相同状态, 让你能在有限的内存容量中 载入更多对象。

应用场景

程序必须支持大量对象且没有足够的内存容量时

应用该模式所获的收益大小取决于使用它的方式和情景。 它在下列情况中最有效:

  • 程序需要生成数量巨大的相似对象
  • 这将耗尽目标设备的所有内存
  • 对象中包含可抽取且能在多个对象间共享的重复状态。

实现方法

1.将需要改写为享元的类成员变量拆分为两个部分:

◦ 内在状态:包含不变的、可在许多对象中重复使用的数据 的成员变量。

◦ 外在状态:包含每个对象各自不同的情景数据的成员变量

2.保留类中表示内在状态的成员变量, 并将其属性设置为不可修改。 这些变量仅可在构造函数中获得初始数值。

3.找到所有使用外在状态成员变量的方法, 为在方法中所用的 每个成员变量新建一个参数, 并使用该参数代替成员变量。

4.你可以有选择地创建工厂类来管理享元缓存池, 它负责在新 建享元时检查已有的享元。 如果选择使用工厂, 客户端就只 能通过工厂来请求享元, 它们需要将享元的内在状态作为参 数传递给工厂。

5.客户端必须存储和计算外在状态(情景)的数值,因为只有 这样才能调用享元对象的方法。 为了使用方便, 外在状态和 引用享元的成员变量可以移动到单独的情景类中。

优缺点

优点:

1.如果程序中有很多相似对象, 那么你将可以节省大量内存。

缺点:

1.你可能需要牺牲执行速度来换取内存, 因为他人每次调用享元方法时都需要重新计算部分情景数据。

2.代码会变得更加复杂。 团队中的新成员总是会问:“为什么要像这样拆分一个实体的状态?”。

结构

UML图

classDiagram
Client o--> Context
Context-->FlyweightFactory
Context -- > Flyweight
Flyweight <--o FlyweightFactory
class Context{
	-uniqueState
	-flyweight
	+Context(repeatingState,uniqueState)
	+operation()
}
class FlyweightFactory{
	-cache:Flyweight[]
	+getFlyweight(repeatingState)
}
class Flyweight{
	-repeatingState
	+operaion(uniqueState)
}

参与者

1.享元(Flyweight)类包含原始对象中部分能在多个对象中共享的状态。 同一享元对象可在许多不同情景中使用。 享元中存储的状态被称为“内在状态”。 传递给享元方法的状态被称为“外在状态”。

2.情景(Context)类包含原始对象中各不相同的外在状态。情景与享元对象组合在一起就能表示原始对象的全部状态。

通常情况下, 原始对象的行为会保留在享元类中。 因此调用 享元方法必须提供部分外在状态作为参数。 但你也可将行为 移动到情景类中, 然后将连入的享元作为单纯的数据对象。

3.客户端(Client)负责计算或存储享元的外在状态。在客户端看来, 享元是一种可在运行时进行配置的模板对象, 具体 的配置方式为向其方法中传入一些情景数据参数。

4.享元工厂(Flyweight Factory)会对已有享元的缓存池进行 管理。 有了工厂后, 客户端就无需直接创建享元, 它们只需 调用工厂并向其传递目标享元的一些内在状态即可。 工厂会 根据参数在之前已创建的享元中进行查找, 如果找到满足条 件的享元就将其返回; 如果没有找到就根据参数新建享元。

享元模式只是一种优化。 在应用该模式之前, 你要确定程序中存在与大量类似对象同时占用内存相关的内存消耗问题,并且确保该问题无法使用其他更好的方式来解决。

通用写法

public class Flyweight {
   private String repeatingState;
​
   public void operation(String uniqueState){
       System.out.println(uniqueState);
  }
}
​
public class FlyweightFactory {
   Flyweight[] cache = new Flyweight[10];
​
   public Flyweight getFlyweight (int repeatingState){
       if (cache[repeatingState] == null){
           cache[repeatingState] = new Flyweight();
      }
       return cache[repeatingState];
  }
}
​
public class Context {
   private String uniqueState;
   private Flyweight flyweight;
​
   public Context(int repeatingState,String uniqueState){
       this.uniqueState = uniqueState;
       this.flyweight = new FlyweightFactory().getFlyweight(repeatingState);
  }
​
   public void operation(){
       flyweight.operation(uniqueState);
  }
}
​
public class Client {
   public static void main(String[] args) {
       Flyweight flyweight = new Flyweight();
       Context context = new Context(1,"hello");
       context.operation();
  }
}