游戏地图上种了很多树。有的在河边,有的在山脚,坐标都不一样。
可仔细看,树的种类就那么几种。绿色橡树长得一样,橙色枫树也用同一张图片。
享元模式就是把这些重复内容共享起来。

每棵树都存一套资料,有点浪费
如果每个树对象都保存坐标、名称、颜色和图片,同一份图片会被重复放很多次。
树少时没啥感觉。对象一多,重复数据就显眼了。
先看关键 UML 图
TreeType 保存可以共享的树种资料。Tree 保存坐标,并引用一个 TreeType。
TreeFactory 像个小仓库,相同树种只创建一次。
内部状态和外部状态
享元模式常提两个词,其实没多玄乎。
内部状态是可以共享的内容,比如树名、颜色、图片。
外部状态是每个对象自己的内容,比如横坐标和纵坐标。
济南话说,把能公用的搁一块,不能公用的各自拿着,就这么个意思。
共享的树种对象
public class TreeType {
private final String name;
private final String color;
public TreeType(String name, String color) {
this.name = name;
this.color = color;
}
public void draw(int x, int y) {
System.out.println(name + "(" + color + ") 坐标=" + x + "," + y);
}
}
坐标没有放进 TreeType,因为同一种树会出现在不同位置。
工厂负责找共享对象
private static final Map<String, TreeType> TYPES = new HashMap<>();
public static TreeType getTreeType(String name, String color) {
String key = name + ":" + color;
return TYPES.computeIfAbsent(
key, k -> new TreeType(name, color));
}
缓存里有绿色橡树,就把原来的对象拿出来。没有时才创建。
每棵树只保存位置
public class Tree {
private final int x;
private final int y;
private final TreeType type;
public Tree(int x, int y, TreeType type) {
this.x = x;
this.y = y;
this.type = type;
}
public void draw() {
type.draw(x, y);
}
}
Tree 很轻,只保存坐标和共享类型的引用。
种 5 棵树看看
Forest forest = new Forest();
forest.plantTree(10, 20, "橡树", "绿色");
forest.plantTree(30, 40, "橡树", "绿色");
forest.plantTree(50, 60, "枫树", "橙色");
forest.plantTree(70, 80, "橡树", "绿色");
forest.plantTree(90, 30, "枫树", "橙色");
这里有 5 个位置不同的 Tree,但 TreeType 只有 2 个。
青岛话说,省下来的不是树,是重复的那份资料,挺恣儿。
什么时候适合用
对象不多时,普通写法更清楚。只有数量很多、重复数据明显时,共享才有价值。
共享对象最好保持不变。大家共用一份数据,谁随手改了,其他对象也会受影响。
狗哥收个尾
享元模式的重点,是 共享重复且稳定的内容。
树的位置各自保存,树种资料集中复用。先分清内部状态和外部状态,这个模式就不难了。
下一篇继续聊 代理模式,看看图片还没真正加载时,怎么先放一个代理顶上。