Skip to main content

享元模式 (Flyweight)

场景问题​

问题案例​

考虑一个文本编辑器需要显示大量字符对象,每个字符都包含字体、颜色等信息。如果每个字符都单独存储全部属性,将极大浪费内存。

不用模式的解决方案​

实现简化版本​

最直接的做法是每个字符对象都保存全部属性:

public class Character {
private char symbol;
private String font;
private int size;
private String color;
public Character(char symbol, String font, int size, String color) {
this.symbol = symbol;
this.font = font;
this.size = size;
this.color = color;
}
// ...getter...
}

使用示例​

List<Character> text = new ArrayList<>();
text.add(new Character('a', "宋体", 12, "黑色"));
text.add(new Character('b', "宋体", 12, "黑色"));
// ...

有何问题​

  1. 相同属性的数据被重复存储,浪费内存。
  2. 当文本量很大时,系统资源消耗极高。

解决方案​

享元模式的定义​

运用共享技术有效地支持大量细粒度对象的复用。

应用享元模式的思路​

  • 将可共享的属性(内部状态)提取出来,多个对象共享。
  • 不可共享的属性(外部状态)由客户端维护。
  • 通过工厂管理享元对象的创建和复用。

享元模式的结构​

  • Flyweight:抽象享元,定义接口。
  • ConcreteFlyweight:具体享元,实现共享逻辑。
  • FlyweightFactory:享元工厂,负责创建和管理享元对象。

享元模式的实现​

// 抽象享元
public interface Flyweight {
void display(int row, int col);
}

// 具体享元
public class CharacterFlyweight implements Flyweight {
private char symbol;
private String font;
public CharacterFlyweight(char symbol, String font) {
this.symbol = symbol;
this.font = font;
}
public void display(int row, int col) {
System.out.println("字符:" + symbol + " 字体:" + font + " 位置:(" + row + "," + col + ")");
}
}

// 享元工厂
import java.util.*;
public class FlyweightFactory {
private Map<String, Flyweight> pool = new HashMap<>();
public Flyweight getFlyweight(char symbol, String font) {
String key = symbol + font;
if (!pool.containsKey(key)) {
pool.put(key, new CharacterFlyweight(symbol, font));
}
return pool.get(key);
}
}

使用示例​

public class Client {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight f1 = factory.getFlyweight('a', "宋体");
Flyweight f2 = factory.getFlyweight('a', "宋体");
System.out.println(f1 == f2); // true,共享对象
f1.display(1, 1);
f2.display(1, 2);
}
}

享元重写示例​

示例结构​


示例代码​

见上文"享元模式的实现"部分。


模式讲解​

享元模式的功能​

  • 大量细粒度对象的高效复用,节省内存。
  • 内部状态共享,外部状态由客户端维护。

实现成接口​

  • 抽象享元通常定义为接口或抽象类。

使用工厂方法​

  • 工厂方法用于统一管理享元对象的创建和复用。

切换产品簇​

  • 可用于切换不同类型的享元对象,如不同字体、颜色等。

执行时序图​