结构型模式之享元

享元(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 }
View Code

测试运行,估计需要运行很长时间。仔细分析,发现可以将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 }
View Code

这里,FlyPoint就是一个享元,不同int和float对值相当于FlyPoint的表现形式,相当于加粗和不加粗的A字母一样,我们将加粗和不加粗这两种表现形式放到一起,与字符A分开,不同的表现形式作用于同一个字母A是展现不同的效果,这样我们就不用创建一个加粗的A字符,需要的时候又创建一个不加粗的A字符,这样创建了两个字符对象,显然如果篇幅较大的话,可能创建很多的A字符,而通过享元模式,则通篇就可以只创建一个A字符!

时间原因,没有自己设计代码,以上内容参考自《Thinking in Patterns》、GOF一书、《Java与模式》

 

原文地址:https://www.cnblogs.com/codeMedita/p/7358053.html