1. 程式人生 > >結構型模式之享元

結構型模式之享元

b- log 格式 問題 gof num 創建 角色 內容

享元(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與模式》

結構型模式之享元