概述
- 缓存、Cache、Flyweight
- 属于结构型模式
- 不在每个对象中保存所有数据,而是共享多个对象所共有的相同状态,实现在有限的内存中载入更多的对象
- 在软件系统采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行(内存)代价
- 如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的形式操作
- 运用共享技术有效地支持大量细粒度对象
- 举例:绝大部分字符可共用字体对象,不用每个字符创建一个字体对象
- 建立表,按字符查找,有字体返回,没字体创建
- 面向对象解决了抽象性问题,但作为一个在机器中的运行实体,需要考虑对象的代价问题
- 注意内部状态和外部状态的划分,否则可能会引起线程安全问题
- 所有类必须有一个工厂对象加以控制
- Flyweight采用对象共享的做法来降低系统中的对象个数,从而降低细粒度对象给系统带来的内存压力,在具体实现方面,要注意对象状态的处理
- 拿到对象先用一下sizeof(),看看它有多大
- 首先评估,如一个对象40k,10w个4M,再决定是否用
场景
- 游戏中,玩家在地图上移动并相互射击,每个子弹都是一个独立对象,战况激烈时,过多的子弹对象导致内存崩溃。每个子弹的颜色、大小等一样且保持不变(内在状态),位置、速度等不一样且不断变化(外在状态)
- 将两种属性分开处理,通过享元对象保存内在状态,而将外在状态传递给依赖于享元对象的一个特殊方法,内在状态只能读取,不能修改,外在状态可以从外部修改
- Game类中的数组容器存储外在对象和享元对象的引用
- 虽然子弹对象的总数是不变的,但占用的空间要小得多
- Java中的String,如果有则返回,没有则创建一个字符串保存在字符串缓存池中
- 数据库的数据池
结构
- 享元类(Flyweight):存储能在多个对象中共享的状态,享元中存储内在状态,传递给享元方法的状态为外在状态
- 情境类(Context):包含原始对象中各不相同的外在状态,与享元对象组合在一起表示原始对象的全部状态
- 客户端(Client):计算或存储享元的外在状态
- 享元工厂(FlyweightFactory):管理已有享元的缓存池,客户端无需直接创建享元,而是通过调用工厂并向其传递目标享元的一些内在状态,工厂根据参数查找已有的享元,如有则将其返回,没有则创建新享元
示例1
Flyweight.cpp
1 class Font { 2 private: 3 4 //unique object key 5 string key; 6 7 //object state 8 //.... 9 10 public: 11 Font(const string& key){ 12 //... 13 } 14 }; 15 16 class FontFactory{ 17 private: 18 map<string,Font* > fontPool; 19 20 public: 21 Font* GetFont(const string& key){ 22 23 map<string,Font*>::iterator item=fontPool.find(key); 24 25 if(item!=footPool.end()){ 26 return fontPool[key]; 27 } 28 else{ 29 Font* font = new Font(key); 30 fontPool[key]= font; 31 return font; 32 } 33 34 } 35 36 void clear(){ 37 //... 38 } 39 };
示例2
1 // 享元类包含一个树的部分状态。这些成员变量保存的数值对于特定树而言是唯一 2 // 的。例如,你在这里找不到树的坐标。但这里有很多树木之间所共有的纹理和颜 3 // 色。由于这些数据的体积通常非常大,所以如果让每棵树都其进行保存的话将耗 4 // 费大量内存。因此,我们可将纹理、颜色和其他重复数据导出到一个单独的对象 5 // 中,然后让众多的单个树对象去引用它。 6 class TreeType is 7 field name 8 field color 9 field texture 10 constructor TreeType(name, color, texture) { ... } 11 method draw(canvas, x, y) is 12 // 1. 创建特定类型、颜色和纹理的位图。 13 // 2. 在画布坐标 (X,Y) 处绘制位图。 14 15 // 享元工厂决定是否复用已有享元或者创建一个新的对象。 16 class TreeFactory is 17 static field treeTypes: collection of tree types 18 static method getTreeType(name, color, texture) is 19 type = treeTypes.find(name, color, texture) 20 if (type == null) 21 type = new TreeType(name, color, texture) 22 treeTypes.add(type) 23 return type 24 25 // 情景对象包含树状态的外在部分。程序中可以创建数十亿个此类对象,因为它们 26 // 体积很小:仅有两个整型坐标和一个引用成员变量。 27 class Tree is 28 field x,y 29 field type: TreeType 30 constructor Tree(x, y, type) { ... } 31 method draw(canvas) is 32 type.draw(canvas, this.x, this.y) 33 34 // 森林(Forest)类是享元的客户端。 35 class Forest is 36 field trees: collection of Trees 37 38 method plantTree(x, y, name, color, texture) is 39 type = TreeFactory.getTreeType(name, color, texture) 40 tree = new Tree(x, y, type) 41 trees.add(tree) 42 43 method draw(canvas) is 44 foreach (tree in trees) do 45 tree.draw(canvas)
示例3
1 import java.util.HashMap; 2 3 public interface Shape { 4 void draw(); 5 } 6 7 public class Circle implements Shape{ 8 private String color; 9 private int x; 10 private int y; 11 private int radius; 12 13 public Circle(String color) { 14 this.color = color; 15 } 16 17 public void setX(int x) { 18 this.x = x; 19 } 20 21 public void setY(int y) { 22 this.y = y; 23 } 24 25 public void setRadius(int radius) { 26 this.radius = radius; 27 } 28 29 @Override 30 public void draw() { 31 System.out.println("Circle : Draw() [Color : " + color + " , x : " 32 + x + ", y : " + y + ", radius : " + radius + "]"); 33 34 } 35 } 36 37 public class ShapeFactory { 38 private static final HashMap<String, Shape> circleMap = new HashMap(); 39 public static Shape getCircle(String color) { 40 Circle circle = (Circle)circleMap.get(color); 41 if(circle == null) { 42 circle = new Circle(color); 43 circleMap.put(color, circle); 44 System.out.println("Creating circle of color : " + color ); 45 } 46 return circle; 47 } 48 } 49 50 public class FlyweightPatternDemo { 51 52 private static final String colors[] = {"Red","Green","Blue","White","Black"}; 53 public static void main(String[] args) { 54 for(int i = 0 ; i < 20 ; ++ i ) { 55 Circle circle = (Circle)ShapeFactory.getCircle(getRandomColor()); 56 circle.setX(getRandomX()); 57 circle.setY(getRandomY()); 58 circle.setRadius(100); 59 circle.draw(); 60 } 61 } 62 private static String getRandomColor() { 63 return colors[(int)(Math.random()*colors.length)]; 64 } 65 private static int getRandomX() { 66 return (int)(Math.random()*100); 67 } 68 private static int getRandomY() { 69 return (int)(Math.random()*100); 70 } 71 }
参考