<大話設計模式>本教程說明及著作權聲明
l 該文檔參考和使用了網路上的免費開放的圖片和內容,並以免費開放的方式發布,希望為移動互連網和智能手機時代貢獻綿薄之力!可以隨意轉載,但不得使用該文檔謀利。
l 如果對該文檔有任何疑問或者建議,請進入官方部落格
http://www.cnblogs.com/guoshiandroid/留言或者直接與國士工作室聯絡(後附連絡方式),我們會謹慎參考您的建議並根據需要對本文檔進行修改,以造福更多開發人員!
l 《大話設計模式》的最新及完整內容會在國士工作室官方部落格定期更新,請訪問國士工作室部落格
http://www.cnblogs.com/guoshiandroid/擷取更多更新內容。
國士工作室是一支專註於Android平台企業級應用開發的技術團隊,致力於做中國最棒的Android應用程式開發機構,提供最棒的Android企業級應用開發培訓服務。
企業培訓和開發合作官方連絡方式:
電話:18610086859
Email:hiheartfirst@gmail.com
QQ:1740415547
QQ群:148325348
國士工作室 有你更美好!
查看其他部分:本教程整體說明及章節索引
本文PDF下載連結
單例模式 你是我的唯一
單例模式應用情境舉例:
“曾經滄海難為水,除卻巫山不是雲”,這句話用現在的語言解釋就是“你是我的唯一”。
GG和MM都是初次戀愛,都把對方視為自己此生的唯一。而且GG和MM都在不斷的向對方學習,不斷的完善自己。GG和MM的甜蜜和幸福很快就轟動了整個院系。男生一般都拿GG的女朋友教育自己的女朋友說別人怎麼怎麼樣,而女生也經常拿MM的男朋友說男生該如何如何做。而且,年級輔導員還在年級會上表揚了GG和MM,說男生都應該想MM的男朋友學習,女生都應該向GG的女朋友學習!呵呵,很顯然,大家都知道,輔導員說GG的女朋友就是指MM,而說MM的男朋友時就是指GG。
單例模式解釋:
GoF對單例模式(Singleton Pattern)的定義是:保證一個類、只有一個執行個體存在,同時提供能對該執行個體加以訪問的全域存取方法。
單例模式是一種對象建立型模式,使用單例模式,可以保證為一個類只產生唯一的執行個體對象。也就是說,在整個程式空間中,該類只存在一個執行個體對象。
單例模式的要點有三個;一是某個類只能有一個執行個體;二是它必須自行建立這個執行個體;三是它必須自行向整個系統提供這個執行個體。
英文定義為:Ensure a class only has one instance, and provide a global point of access to it.
單例模式的UML圖:
單例模式比較的單純,其UML圖如下所示:
單例模式深入分析:
單例模式的要點有三個;一是某個類只能有一個執行個體;二是它必須自行建立這個執行個體;三是它必須自行向整個系統提供這個執行個體。
單例模式適合於一個類只有一個執行個體的情況,比如視窗管理器,列印緩衝池和檔案系統,它們都是原型的例子。典型的情況是,那些對象的類型被遍及一個軟體系統的不同對象訪問,因此需要一個全域的訪問指標,這便是眾所周知的單例模式的應用。當然這隻有在你確信你不再需要任何多於一個的執行個體的情況下
在電腦系統中,需要管理的資源套件括軟體外部資源,譬如每台電腦可以有若干個印表機,但只能有一個Printer Spooler, 以避免兩個列印工作同時輸出到印表機中。每台電腦可以有若干傳真卡,但是只應該有一個軟體負責管理傳真卡,以避免出現兩份傳真作業同時傳到傳真卡中的情況。每台電腦可以有若干通訊連接埠,系統應當集中管理這些通訊連接埠,以避免一個通訊連接埠同時被兩個請求同時調用。
需要管理的資源也包括軟體內部資源,譬如,大多數的軟體都有一個(甚至多個)屬性(properties)檔案存放系統配置。這樣的系統應當由一個對象來管理一個屬性檔案。
需要管理的軟體內部資源也包括譬如負責記錄網站來訪人數的組件,記錄軟體系統內部事件、出錯資訊的組件,或是對系統的表現進行檢查的組件等。這些組件都必須集中管理。
單例模式使用情境分析及代碼實現:
在上面的使用情境中,無論是誰叫GG的女朋友,大家都知道只的是MM;而相應的,無論是誰說MM的男朋友,大家都知道是GG。GG和MM分別都是對方單例O(∩_∩)O哈哈~
UML模型圖如下所示:
筆者在這裡以MM的男朋友GG為例進行單例模式的說明。
GG單例模式的第一個版本,採用的是“餓漢式”,也就是當類載入進來的就立即執行個體化GG對象,但是這種方式比較的消耗電腦資源。具體實現代碼如下:
package com.diermeng.designPattern.Singleton; /* * GG單例模式的第一個版本 為“餓漢式” */ public class GGVersionOne { //在類被載入進入記憶體的時候就建立單一的GG對象 public static final GGVersionOne gGVersionOne = new GGVersionOne(); //名稱屬性 private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } //建構函式私人化 private GGVersionOne() { } //提供一個全域的靜態方法 public static GGVersionOne getGG() { return gGVersionOne; } } |
GG單例模式的第二個版本:“懶漢式”,在單線程下能夠非常好的工作,但是在多線程下存線上程安全問題,具體代碼如下:
package com.diermeng.designPattern.Singleton; /* * GG單例模式的第二個版本 採用“懶漢式” 在需要使用的時候才執行個體化GG */ public class GGVersionTwo { //GG的姓名 private String name; //對單例本身引用的名稱 private static GGVersionTwo gGVersionTwo; public String getName() { return name; } public void setName(String name) { this.name = name; } //建構函式私人化 private GGVersionTwo() { } //提供一個全域的靜態方法 public static GGVersionTwo getGG() { if(gGVersionTwo == null) { gGVersionTwo = new GGVersionTwo(); } return gGVersionTwo; } } |
GG單例模式的第三個版本,為解決多線程問題,採用了對函數進行同步的方式,但是比較浪費資源,因為每次都要進行同步檢查,而實際中真正需要檢查只是第一次執行個體化的時候,具體代碼如下所示:
package com.diermeng.designPattern.Singleton; /* * GG單例模式的第三個版本 對函數進行同步 */ public class GGVersionThree { //GG的姓名 private String name; //對單例本身引用的名稱 private static GGVersionThree gGVersionThree; public String getName() { return name; } public void setName(String name) { this.name = name; } //建構函式私人化 private GGVersionThree() { } //提供一個全域的靜態方法,使用同步方法 public static synchronized GGVersionThree getGG() { if(gGVersionThree == null) { gGVersionThree = new GGVersionThree(); } return gGVersionThree; } } |
GG單例模式第四個版本,既解決了“懶漢式的”多線程問題,又解決了資源浪費的現象,看上去是一種不錯的選擇,具體代碼如下所示:
package com.diermeng.designPattern.Singleton; /* * GG單例模式的第四個版本,既解決了“懶漢式的”多線程問題,又解決了資源浪費的現象,看上去是一種不錯的選擇 */ public class GGVersionFour { //GG的姓名 private String name; //對單例本身引用的名稱 private static GGVersionFour gGVersionFour; public String getName() { return name; } public void setName(String name) { this.name = name; } //建構函式私人化 private GGVersionFour() { } //提供一個全域的靜態方法 public static GGVersionFour getGG() { if(gGVersionFour == null) { synchronized (GGVersionFour.class) { if(gGVersionFour == null) { gGVersionFour = new GGVersionFour(); } } } return gGVersionFour; } } |
最後我們建立測試用戶端測試一下版本四:
package com.diermeng.designPattern.Singleton.client; import com.diermeng.designPattern.Singleton.GGVersionFour; /* * 測試用戶端 */ public class SingletonTest { public static void main(String[] args) { //執行個體化 GGVersionFour gG1 = GGVersionFour.getGG(); GGVersionFour gG2 = GGVersionFour.getGG(); //設值 gG1.setName("GGAlias"); gG2.setName("GG"); System.out.println(gG1.getName()); System.out.println(gG2.getName()); } } |
輸出的結果如下:
單例模式的優缺點分析:
優點:用戶端使用單例模式類的執行個體的時候,只需要調用一個單一的方法即可產生一個唯一的執行個體,有利於節約資源。
缺點:首先單例模式很難實現序列化,這就導致採用單例模式的類很難被持久化,當然也很難通過網路傳輸;其次由於單例採用靜態方法,無法在繼承結構中使用。最後如果在分布式叢集的環境中存在多個Java虛擬機器的情況下,具體確定哪個單例在運行也是很困難的事情。
單例模式的實際應用簡介:
單例模式一般會出現在以下情況下:
在多個線程之間,比如servlet環境,共用同一個資源或者操作同一個對象
在整個程式空間使用全域變數,共用資源
大規模系統中,為了效能的考慮,需要節省對象的建立時間等等。
溫馨提示:
細心的讀者可能會發現,筆者在寫單例模式的雙重檢查方式的使用了“看上去是一種不錯的選擇”之語,之所以樣說,是因為:Java的線程工作順序是不確定的,這就會導致在多線程的情況沒有執行個體化就使用的現象,進而導致程式崩潰。不過雙重檢查在C語言中並沒有問題。因為大師說:雙重檢查對Java語言並不是成立的。儘管如此,雙重檢查仍然不失為解決多線程情況下單例模式的一種理想的方案。