享元模式 (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, "黑色"));
// ...
有何问题
- 相同属性的数据被重复存储,浪费内存。
- 当文本量很大时,系统资源消耗极高。
解决方案
享元模式的定义
运用共享技术有效地支持大量细粒度对象的复用。
应用享元模式的思路
- 将可共享的属性(内部状态)提取出来,多个对象共享。
- 不可共享的属性(外部状态)由客户端维护。
- 通过工厂管理享元对象的创建和复用。
享元模式的结构
- 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);
}
}
享元重写示例
示例结构
示例代码
见上文"享元模式的实现"部分。
模式讲解
享元模式的功能
- 大量细粒度对象的高效复用,节省内存。
- 内部状态共享,外部状态由客户端维护。
实现成接口
- 抽象享元通常定义为接口或抽象类。
使用工厂方法
- 工厂方法用于统一管理享元对象的创建和复用。
切换产品簇
- 可用于切换不同类型的享元对象,如不同字体、颜色等。