享元模式的核心是共享细粒度对象,以减少内存使用和对象创建的开销。这和对象池有相似之处,但享元更侧重于共享内在状态,而将可变部分作为外部状态传递。
关键点包括内在状态和外在状态的区分,工厂类管理共享对象,以及如何实现对象的复用。
享元模式可以作为另一种工具,特别是在处理大量相似对象时。比如,游戏中大量重复的棋子类型,或者文档中的字符对象,这些场景下享元模式非常适用。
需要注意的是,享元模式适用于对象的大部分状态可以外部化的情况。如果对象的状态经常变化,可能不适合使用享元。此外,线程安全也是一个考虑点,因为共享对象可能被多个线程访问。
最后,总结适用场景和优缺点,帮助用户判断何时使用享元模式。例如,在需要处理大量相似对象且内存占用高时,享元模式能显著优化性能。但如果对象状态变化频繁或难以区分内外状态,可能不适用。
核心思想:通过共享技术减少大量细粒度对象的创建开销,将对象的**内在状态(不变部分)与外在状态(可变部分)**分离,仅存储一份内在状态,外在状态由客户端传递。
内在状态(Intrinsic State)
外在状态(Extrinsic State)
享元工厂(Flyweight Factory)
+-------------------+ +---------------------+
| Flyweight | | FlyweightFactory |
+-------------------+ +---------------------+
| +operation(extr) |<------->| -pool: Map |
+-------------------+ | +getFlyweight(key) |
^ +---------------------+
|
+-------------------+
| ConcreteFlyweight |
+-------------------+
| -intrinsicState |
+-------------------+
| +operation(extr) |
+-------------------+
假设需要渲染大量文本字符,每个字符的字体、颜色等属性固定(内在状态),但位置、大小可变(外在状态)。
public interface CharacterFlyweight {
void render(int x, int y); // 外在状态由参数传递
}
public class ConcreteCharacter implements CharacterFlyweight {
// 内在状态(不变)
private final char character;
private final String font;
private final String color;
public ConcreteCharacter(char character, String font, String color) {
this.character = character;
this.font = font;
this.color = color;
}
@Override
public void render(int x, int y) {
System.out.printf("Render '%s' at (%d,%d) with font=%s, color=%s\n",
character, x, y, font, color);
}
}
import java.util.HashMap;
import java.util.Map;
public class CharacterFactory {
private static final Map<String, CharacterFlyweight> pool = new HashMap<>();
public static CharacterFlyweight getCharacter(char c, String font, String color) {
// 生成唯一键:组合内在状态
String key = c + "-" + font + "-" + color;
if (!pool.containsKey(key)) {
pool.put(key, new ConcreteCharacter(c, font, color));
}
return pool.get(key);
}
}
public class Client {
public static void main(String[] args) {
// 获取共享的享元对象
CharacterFlyweight charA = CharacterFactory.getCharacter('A', "Arial", "Red");
CharacterFlyweight charB = CharacterFactory.getCharacter('B', "Times New Roman", "Blue");
CharacterFlyweight charA2 = CharacterFactory.getCharacter('A', "Arial", "Red"); // 复用 charA
// 渲染字符,传递外在状态(位置)
charA.render(10, 20);
charB.render(30, 40);
charA2.render(50, 60); // 复用 charA 的内在状态
}
}
Render 'A' at (10,20) with font=Arial, color=Red
Render 'B' at (30,40) with font=Times New Roman, color=Blue
Render 'A' at (50,60) with font=Arial, color=Red
特性 | 享元模式 | 对象池 |
---|---|---|
目标 | 减少内存占用,共享不变状态 | 复用可变对象,减少创建开销 |
状态管理 | 内在状态共享,外在状态由客户端传递 | 对象状态可能被重置或复用 |
适用场景 | 大量相似对象,内在状态可分离(如字符、UI控件) | 对象创建成本高(如数据库连接、线程) |