<大話設計模式>本教程說明及著作權聲明
國士工作室是一支專註於Android平台企業級應用開發的技術團隊,致力於做中國最棒的Android應用程式開發機構,提供最棒的Android企業級應用開發培訓服務。
企業培訓和開發合作官方連絡方式:
電話:18610086859
Email:hiheartfirst@gmail.com
QQ:1740415547
國士工作室 有你更美好!
l 該文檔參考和使用了網路上的免費開放的圖片和內容,並以免費開放的方式發布,希望為移動互連網和智能手機時代貢獻綿薄之力!可以隨意轉載,但不得使用該文檔謀利。
l 如果對該文檔有任何疑問或者建議,請進入官方部落格
http://www.cnblogs.com/guoshiandroid/留言或者直接與國士工作室聯絡(後附連絡方式),我們會謹慎參考您的建議並根據需要對本文檔進行修改,以造福更多開發人員!
l 《大話設計模式》的最新及完整內容會在國士工作室官方部落格定期更新,請訪問國士工作室部落格
http://www.cnblogs.com/guoshiandroid/擷取更多更新內容。
享元模式 簡訊可以這樣發
享元模式應用情境舉例:
GG每天給MM至少發一條簡訊,而且每天入睡前是必有一條簡訊的,往往是一些瑣事和一些比較肉麻的情話。開始的一個月,GG還對此是樂不可支,隨著時間的推移,那些肉麻的話說了很多遍,自己也覺得厭煩了,而且更讓人不可忍耐的是這些肉麻的情話每次都要重複的輸入。GG把這一煩心事告訴了自己的好友K,K說,“你這個大傻瓜,怎麼不把一些你常用的話存放在你的手機中,這樣,要用的時候,直接拿來用就行了”,傻GG一聽,頓時覺得醍醐灌頂,於是立即在手機中存放入了“寶貝兒,晚安喔”、“你是我天使”,“寶貝兒,我永遠的愛你”等話語。
享元模式解釋:
享元模式(Flyweight Pattern)是通過使用共用的方式,達到高效地支援大量的細粒度對象。它的目的就是節省佔用的空間資源,從而實現系統效能的改善。
享元的英文是Flyweight,它是一個來自於體育方面的專業用語,在拳擊、摔跤和舉重比賽中特指最輕量的層級。把這個單詞移植到軟體工程裡面,也是用來表示特別小的對象,即細粒度對象。至於為什麼我們把Flyweight翻譯為“享元”,可以理解為共用元對象,也就是共用細粒度對象。
英文定義為:Use sharing to support large numbers of fine-grained objects
efficiently.
享元模式的UML圖:
享元模式涉及以下的角色:
抽象享元(Flyweight)角色: 它是所有具體享元類的超類。為這些類規定出需要實現的公用介面,那些需要外蘊狀態(External State)的操作可以通過方法的參數傳入。抽象享元的介面使得享元變得可能,但是並不強制子類實行共用,因此並非所有的享元對象都是可以共用的。
具體享元(ConcreteFlyweight)角色:具體享元類實現了抽象享元類所規定的介面。如果有內蘊狀態(Internal State)的話,必須負責為內蘊狀態提供儲存空間。享元對象的內蘊狀態必須與對象所處的周圍環境無關,從而使得享元對象可以在系統內共用。有時候具體享元類又稱為單純具體享元類,因為複合享元類是由單純具體享元角色通過複合而成的。
不能共用的具體享元類(UnsharableFlyweight): 不能共用的享元類,又叫做複合享元類。一個複合享元對象是由多個單享元對象組成,這些組成的對象是可以共用的,但是複合享元類本身並不能共用。
享元工廠類(FlyweightFactory): 享元工廠類負責建立和管理享元對象。當一個用戶端對象請求一個享元對象的時候,享元工廠需要檢查系統中是否已經有一個符合要求的享元對象,如果已經有了,享元工廠角色就應當提供這個已有的享元對象;如果系統中沒有適當的享元對象的話,享元工廠角色就應當建立一個新的合適的享元對象。
客戶類(Client): 客戶類需要自行儲存所有享元對象的外蘊狀態。
享元模式的UML圖如下所示:
享元模式深入分析:
它通過與其他類似對象共用資料來減小記憶體佔用。
享元對象的第一類狀態稱為內蘊狀態(Internal State)。它不會隨環境改變而改變,儲存在享元對象內部,因此內蘊狀態是可以共用的,對於任何一個享元對象來講,它的值是完全相同的。
享元對象的第二類狀態稱為外蘊狀態(External
State)。它會隨環境的改變而改變,因此是不可以共用的狀態,對於不同的享元對象來講,它的值可能是不同的。享元對象的外蘊狀態必須由用戶端儲存,在享元對象被建立之後,需要使用的時候再傳入到享元對象內部。所以享元的外蘊狀態與內蘊狀態是兩類相互獨立的狀態,彼此沒有關聯。
享元模式使用情境分析及代碼實現:
在上面的使用情境中,儲存在GG手機中的肉麻簡訊就是具體的享元對象。
UML模型圖如下所示:
建立抽象享元角色:
package com.diermeng.designPattern.Flyweight; /* * 抽象享元角色 */ public interface BaseSweetWord { //顯示方法 public void display(); } |
建立具體享元角色:
package com.diermeng.designPattern.Flyweight.impl; import com.diermeng.designPattern.Flyweight.BaseSweetWord; /* * 具體的享元類 */ public class MySweetWord implements BaseSweetWord{ //具體享元屬性 private String mychar; public MySweetWord() {} //具有執行個體化屬性功能的構造方法 public MySweetWord(String mychar) { this.mychar = mychar; } //對現實功能的具體實現 public void display() { System.out.println(mychar); } } |
建立享元工廠類:
package com.diermeng.designPattern.Flyweight.impl; import java.util.HashMap; import java.util.Map; import com.diermeng.designPattern.Flyweight.BaseSweetWord; /* * 享元工廠類 */ public class MySweetWordFactory { //享元對象的集合 private Map<String,BaseSweetWord> pool; //建構函式 執行個體化享元對象的集合 public MySweetWordFactory() { pool = new HashMap<String,BaseSweetWord>(); } //擷取享元對象 如果集合中不存在該享元對象 就建立這個對象 public BaseSweetWord getMyCharacter(String character) { BaseSweetWord myChar = pool.get(character); if(myChar == null) { myChar = new MySweetWord(character); pool.put(character, myChar); } return myChar; } } |
最後我們建立測試用戶端:
package com.diermeng.designPattern.Flyweight.client; import com.diermeng.designPattern.Flyweight.BaseSweetWord; import com.diermeng.designPattern.Flyweight.impl.MySweetWordFactory; /* * 享元模式測試用戶端 */ public class FlyweightTest { public static void main(String[] args) { //建立享元工廠 MySweetWordFactory factory = new MySweetWordFactory(); //從具體的享元工廠中取出相應的MyCharacter BaseSweetWord myChar1 = factory.getMyCharacter("寶貝兒,晚安喔"); BaseSweetWord myChar2 = factory.getMyCharacter("你是我天使"); BaseSweetWord myChar3 = factory.getMyCharacter("寶貝兒,晚安喔"); BaseSweetWord myChar4 = factory.getMyCharacter("寶貝兒,我永遠的愛你"); myChar1.display(); myChar2.display(); myChar3.display(); myChar4.display(); if(myChar1 == myChar3) { System.out.println("哈哈,恭喜!我們是同一句話,直接複製就行了!"); } else { System.out.println("哎呀,我們不是同一句話!"); } } } |
輸出的結果如下:
寶貝兒,晚安喔 你是我天使 寶貝兒,晚安喔 寶貝兒,我永遠的愛你 哈哈,恭喜!我們是同一句話,直接複製就行了! |
享元模式的優缺點分析:
優點:
使用享元模式可以降低記憶體中對象的數量,從而為系統節省大量的記憶體空間。
缺點:
享元模式使得系統更加複雜,因為為了使對象可以共用,需要將一些狀態外部化,這使得程式的邏輯複雜化。而且,由於享元工廠需要維護所有的享元對象,此時,如果要維護的享元對象很多的話,在尋找具體的享元對象的時候就要消耗大量的時間,換句話說,享元模式是一種以時間換空間的模式。
享元模式的實際應用簡介:
享元模式在一般的項目開發中並不常用,而是常常應用於系統底層的開發,以便解決系統的效能問題。
Java中的String類型就是使用了享元模式。
如果在Java中已經建立了一個字串對象string1,那麼下次再建立相同的字串string2的時候,系統只是把string2的引用指向string1所引用的具體對象,這就實現了相同字串在記憶體中的共用。如果每次執行string1=“abc”操作的時候,都建立一個新的字串對象的話,那麼記憶體的開銷會很大。
如果大家有興趣的話,可以用下面的程式進行測試,就會知道string1和string2的引用是否一致:
String string1= "愛你一萬年,愛你的心永不改變";
String string2= "愛你一萬年,愛你的心永不改變";
//“==”用來判斷兩個對象是否是同一個,equals判斷字串的值是否相等
if( string1 == string2 ){
System.out.println("兩者一樣");
}else{
System.out.println("兩者不一樣");
}
程式運行後,輸出的結果為“兩者一樣”,這說明String類的設計採用了享元模式。如果string1的內容發生了變化,比如執行了string1 += "讓我們結婚吧!"的語句,那麼s1與s2的引用將不再一致。
我們額外的談一下PHP中String的處理。作為一種弱類型語言,PHP的字串類型是一種基本類型,不是對象。另外,它的執行方式與Java有明顯區別,每一個指令檔執行開始,將會裝入所有需要的資源;執行結束後,又將佔用的資源就立即全部釋放,所以它基本上不會產生類似的效能問題,它的字串處理的設計,自然也使用不到享元模式。
溫馨提示:
物件導向雖然很好地解決了抽象性的問題,但是對於一個實際啟動並執行軟體系統,我們還需要考慮物件導向的代價問題,享元模式解決的就是物件導向的代價問題。享元模式採用對象共用的做法來降低系統中對象的個數,從而降低細粒度對象給系統帶來的記憶體壓力。
在具體實現方面,我們要注意對象狀態的處理,一定要正確地區分對象的內蘊狀態和外蘊狀態,這是實現享元模式的關鍵所在。
享元模式的優點在於它大幅度地降低記憶體中對象的數量。為了做到這一點,享元模式也付出了一定的代價:
1、享元模式為了使對象可以共用,它需要將部分狀態外部化,這使得系統的邏輯變得複雜。
2、享元模式將享元對象的部分狀態外部化,而讀取外部狀態使得已耗用時間會有所加長。
另外,還有一個比較令人關心的問題:到底系統需要滿足什麼樣的條件才能使用享元模式。對於這個問題,總結出以下幾點:
1、一個系統中存在著大量的細粒度對象;
2、這些細粒度對象耗費了大量的記憶體。
3、這些細粒度對象的狀態中的大部分都可以外部化;
4、這些細粒度對象可以按照內蘊狀態分成很多的組,當把外蘊對象從對象中剔除時,每一個組都可以僅用一個對象代替。
5、軟體系統不依賴於這些對象的身份,換言之,這些對象可以是不可分辨的。
滿足以上的這些條件的系統可以使用享元對象。最後,使用享元模式需要維護一個記錄了系統已有的所有享元的雜湊表,也稱之為對象池,而這也需要耗費一定的資源。因此,應當在有足夠多的享元執行個體可供共用時才值得使用享元模式。