結構型模式之享元
享元(Flyweight)模式是有關改善性能的一種方法,GOF對享元的功能定義是:運用共享技術有效地支持大量細粒度的對象。為了做到共享,首先要區分內部狀態(Internal State)和外部狀態(External State)。內部狀態是存儲在享元對象的內部,不隨環境的變化而有所不同,因而可以共享這些享元;外部狀態是隨環境改變而變化的,不可共享的狀態。GOF一書中舉了一個例子:文檔編輯器問題,一個編輯器通常提供多種格式的字體,每一個字符都是作為一個對象嵌入到編輯器中,這時候如果把特定的格式和特定的字符關聯起來,則需要創建很多對象,顯然功能復用設計要求相違背;這時候可以將每個字符做成一個享元對象
這其中有一個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) { thisView Code.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 }View Code
這裏,FlyPoint就是一個享元,不同int和float對值相當於FlyPoint的表現形式,相當於加粗和不加粗的A字母一樣,我們將加粗和不加粗這兩種表現形式放到一起,與字符A分開,不同的表現形式作用於同一個字母A是展現不同的效果,這樣我們就不用創建一個加粗的A字符,需要的時候又創建一個不加粗的A字符,這樣創建了兩個字符對象,顯然如果篇幅較大的話,可能創建很多的A字符,而通過享元模式,則通篇就可以只創建一個A字符!
時間原因,沒有自己設計代碼,以上內容參考自《Thinking in Patterns》、GOF一書、《Java與模式》
結構型模式之享元