一、场景
- 有时候,一个程序要创建大量相似的对象,例如,游戏中需要渲染很多的子弹,但这些子弹除了位置不同外,其余都相同。
- 如果我们不将相同的部分抽取出来,迟早会:Out of Memory。
- 享元模式会将不同对象的相同数据进行缓存以节省内存。
我理解:享元,共享元数据。
当然了也有其他称呼:缓存、Cache、Flyweight
1、简单的案例
- “渲染”一片森林,森林由树组成。
@AllArgsConstructor
public class Tree {
private int x;
private int y;
private String name;
private String color;
public int memorySize() {
// int按1个单位估算,String按字符个数估算。
return 2 + name.length() + color.length();
}
}
public class Forest {
private List<Tree> trees;
public Forest() {
trees = new ArrayList<>();
}
public void addTree(Tree tree) {
trees.add(tree);
}
public void printMemorySize() {
System.out.println("memory size: " + trees.stream().mapToInt(Tree::memorySize).sum());
}
}
public class Application {
public static void main(String[] args) {
Forest forest = new Forest();
forest.addTree(new Tree(1, 2, "apple tree", "red"));
forest.addTree(new Tree(3, 4, "apple tree", "red"));
forest.addTree(new Tree(5, 6, "apple tree", "red"));
forest.addTree(new Tree(7, 8, "orange tree", "yellow"));
forest.addTree(new Tree(9, 10, "orange tree", "yellow"));
forest.addTree(new Tree(11, 12, "orange tree", "yellow"));
forest.printMemorySize();
}
}
memory size: 102
二、通过享元模式实现
1、代码
public class Tree {
private int x;
private int y;
private final TreeType treeType;
public Tree(int x, int y, String name, String color) {
this.x = x;
this.y = y;
this.treeType = TreeTypeFactory.gotTreeType(name, color);
}
public int memorySize() {
// int按1个单位估算,String按字符个数估算。
return 2;
}
}
@AllArgsConstructor
public class TreeType {
private String name;
private String color;
public int memorySize() {
return name.length() + color.length();
}
}
public class TreeTypeFactory {
private static final Map<String, TreeType> treeTypeMap = new HashMap<>();
public static TreeType gotTreeType(String name, String color) {
String key = String.format("%s_%s", name, color);
if (!treeTypeMap.containsKey(key)) {
treeTypeMap.put(key, new TreeType(name, color));
}
return treeTypeMap.get(key);
}
public static int memorySize() {
return treeTypeMap.values().stream().mapToInt(TreeType::memorySize).sum();
}
}
public class Application {
public static void main(String[] args) {
Forest forest = new Forest();
forest.addTree(new Tree(1, 2, "apple tree", "red"));
forest.addTree(new Tree(3, 4, "apple tree", "red"));
forest.addTree(new Tree(5, 6, "apple tree", "red"));
forest.addTree(new Tree(7, 8, "orange tree", "yellow"));
forest.addTree(new Tree(9, 10, "orange tree", "yellow"));
forest.addTree(new Tree(11, 12, "orange tree", "yellow"));
forest.printMemorySize();
}
}
/*
memory size: 42
*/
2、个人思考
- 初学外观模式时,我惊呼:这也算设计模式?现在学了享元模式,内心已经很平静了:有应用场景的设计模式就是好模式。
享元模式的关键步骤
:- (1)将类中可以共享的数据抽取出一个享元类。
- Tree类的(name、color)可以被共享,因此抽取为TreeType。
- (2)在创建对象时,共享享元对象。
public Tree(int x, int y, String name, String color) {
this.x = x;
this.y = y;
this.treeType = TreeTypeFactory.gotTreeType(name, color);
}
- (3)通过工厂模式封装创建享元对象的细节:
public class TreeTypeFactory {
private static final Map<String, TreeType> treeTypeMap = new HashMap<>();
...
}
享元模式只有一个目的: 减少内存消耗。 如果程序没有遇到内存容量不足的问题, 则可以暂时忽略该模式。