首页 > 代码库 > 结构型模式之享元
结构型模式之享元
享元(Flyweight)模式是有关改善性能的一种方法,GOF对享元的功能定义是:运用共享技术有效地支持大量细粒度的对象。为了做到共享,首先要区分内部状态(Internal State)和外部状态(External State)。内部状态是存储在享元对象的内部,不随环境的变化而有所不同,因而可以共享这些享元;外部状态是随环境改变而变化的,不可共享的状态。GOF一书中举了一个例子:文档编辑器问题,一个编辑器通常提供多种格式的字体,每一个字符都是作为一个对象嵌入到编辑器中,这时候如果把特定的格式和特定的字符关联起来,则需要创建很多对象,显然功能复用设计要求相违背;这时候可以将每个字符做成一个享元对象,享元的内部状态就是这个字符本身,外部状态则是字符位置、风格等外在表现形式。如文档中可能出现字符a,虽然a的位置和风格可能不同,但是使用的都是同一个字符对象,这样这个a字符对象就可以在整个系统中共享,而不是每一处都创建一个a字符对象。享元模式的结构如下:
这其中有一个UnsharedConcreteFlyweight类,它是复合享元角色,不可共享,复合享元对象可以分解成多个本身是享元对象的组合。
现在给出一个示例代码,这段代码取自于《Thinking In Patterns》,考虑一个 DataPoint 对象,它包含一个 int 和 float类型的数据成员,还有一个 id 数据成员用来表示对象编号。假设你需要创建一百万个这样的对象,然后对它们进行操作,像下面这样:
1 class DataPoint { 2 private static int count = 0; 3 private int id = count++; 4 private int i; 5 private float f; 6 public int getI() { return i; } 7 public void setI(int i) { this.i = i; } 8 public float getF() { return f; } 9 public void setF(float f) { this.f = f; } 10 public String toString() { 11 return "id: " + id + ", i = " + i + ", f = " + f; 12 } 13 } 14 15 public class Test{ 16 static final int size = 1000000; 17 public static void main(String[] args) { 18 DataPoint[] array = new DataPoint[size]; 19 for(int i = 0; i < array.length; i++) 20 array[i] = new DataPoint(); 21 for(int i = 0; i < array.length; i++) { 22 DataPoint dp = array[i]; 23 dp.setI(dp.getI() + 1); 24 dp.setF(47.0f); 25 } 26 System.out.println(array[size -1]); 27 } 28 }
测试运行,估计需要运行很长时间。仔细分析,发现可以将int和float数据外化出去,并且提供对这些数据的操作方法,没必要将每一对int和float的数据存储到一个对象中,完全可以将这些数据存储到一个类中,该类提供对这些数据的操作,这样只需要创建一个对象就可以满足需求了。如下所示:
1 class ExternalizedData { 2 static final int size = 5000000; 3 static int[] id = new int[size]; 4 static int[] i = new int[size]; 5 static float[] f = new float[size]; 6 static { 7 for(int i = 0; i < size; i++) 8 id[i] = i; 9 } 10 } 11 class FlyPoint { 12 private FlyPoint() {} 13 public static int getI(int obnum) { 14 return ExternalizedData.i[obnum]; 15 } 16 public static void setI(int obnum, int i) { 17 ExternalizedData.i[obnum] = i; 18 } 19 public static float getF(int obnum) { 20 return ExternalizedData.f[obnum]; 21 } 22 public static void setF(int obnum, float f) { 23 ExternalizedData.f[obnum] = f; 24 } 25 public static String str(int obnum) { 26 return "id: " + 27 ExternalizedData.id[obnum] + 28 ", i = " + 29 ExternalizedData.i[obnum] + 30 ", f = " + 31 ExternalizedData.f[obnum]; 32 } 33 } 34 35 public class Test{ 36 public static void main(String[] args) { 37 for(int i = 0; i < ExternalizedData.size; i++) { 38 FlyPoint.setI(i, FlyPoint.getI(i) + 1); 39 FlyPoint.setF(i, 47.0f); 40 } 41 System.out.println(FlyPoint.str(ExternalizedData.size -1)); 42 } 43 }
这里,FlyPoint就是一个享元,不同int和float对值相当于FlyPoint的表现形式,相当于加粗和不加粗的A字母一样,我们将加粗和不加粗这两种表现形式放到一起,与字符A分开,不同的表现形式作用于同一个字母A是展现不同的效果,这样我们就不用创建一个加粗的A字符,需要的时候又创建一个不加粗的A字符,这样创建了两个字符对象,显然如果篇幅较大的话,可能创建很多的A字符,而通过享元模式,则通篇就可以只创建一个A字符!
时间原因,没有自己设计代码,以上内容参考自《Thinking in Patterns》、GOF一书、《Java与模式》
结构型模式之享元