单件模式(Singleton Pattern)(转自TerryLee)
概述?
Singleton模式要求一個類有且僅有一個實例,并且提供了一個全局的訪問點。這就提出了一個問題:如何繞過常規的構造器,提供一種機制來保證一個類只有一個實例?客戶程序在調用某一個類時,它是不會考慮這個類是否只能有一個實例等問題的,所以,這應該是類設計者的責任,而不是類使用者的責任。
從另一個角度來說,Singleton模式其實也是一種職責型模式。因為我們創建了一個對象,這個對象扮演了獨一無二的角色,在這個單獨的對象實例中,它集中了它所屬類的所有權力,同時它也肩負了行使這種權力的職責!
意圖
保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
模型圖
邏輯模型圖:
物理模型圖:
生活中的例子
美國總統的職位是Singleton,美國憲法規定了總統的選舉,任期以及繼任的順序。這樣,在任何時刻只能由一個現任的總統。無論現任總統的身份為何,其頭銜"美利堅合眾國總統"是訪問這個職位的人的一個全局的訪問點。
五種實現?
1.簡單實現?
?
public?sealed?class?Singleton{
?? ?static?Singleton?instance=null;
?? ?Singleton()
?? ?{
?? ?}
?? ?public?static?Singleton?Instance
?? ?{
?? ? ? ?get
?? ? ? ?{
?? ? ? ? ? ?if?(instance==null)
?? ? ? ? ? ?{
?? ? ? ? ? ? ? ?instance?=?new?Singleton();
?? ? ? ? ? ?}
?? ? ? ? ? ?return?instance;
?? ? ? ?}
?? ?}
}
這種方式的實現對于線程來說并不是安全的,因為在多線程的環境下有可能得到Singleton類的多個實例。如果同時有兩個線程去判斷(instance == null),并且得到的結果為真,這時兩個線程都會創建類Singleton的實例,這樣就違背了Singleton模式的原則。實際上在上述代碼中,有可能在計算出表達式的值之前,對象實例已經被創建,但是內存模型并不能保證對象實例在第二個線程創建之前被發現。
該實現方式主要有兩個優點:
- 由于實例是在?Instance?屬性方法內部創建的,因此類可以使用附加功能(例如,對子類進行實例化),即使它可能引入不想要的依賴性。
- 直到對象要求產生一個實例才執行實例化;這種方法稱為“惰性實例化”。惰性實例化避免了在應用程序啟動時實例化不必要的?singleton。
2.安全的線程?
public?sealed?class?Singleton{
?? ?static?Singleton?instance=null;
?? ?static?readonly?object?padlock?=?new?object();
?? ?Singleton()
?? ?{
?? ?}
?? ?public?static?Singleton?Instance
?? ?{
?? ? ? ?get
?? ? ? ?{
?? ? ? ? ? ?lock?(padlock)
?? ? ? ? ? ?{
?? ? ? ? ? ? ? ?if?(instance==null)
?? ? ? ? ? ? ? ?{
?? ? ? ? ? ? ? ? ? ?instance?=?new?Singleton();
?? ? ? ? ? ? ? ?}
?? ? ? ? ? ? ? ?return?instance;
?? ? ? ? ? ?}
?? ? ? ?}
?? ?}
}
?
這種方式的實現對于線程來說是安全的。我們首先創建了一個進程輔助對象,線程在進入時先對輔助對象加鎖然后再檢測對象是否被創建,這樣可以確保只有一個實例被創建,因為在同一個時刻加了鎖的那部分程序只有一個線程可以進入。這種情況下,對象實例由最先進入的那個線程創建,后來的線程在進入時(instence == null)為假,不會再去創建對象實例了。但是這種實現方式增加了額外的開銷,損失了性能。
3.雙重鎖定
public?sealed?class?Singleton?{
?? ? static?Singleton?instance=null;
?? ? static?readonly?object?padlock?=?new?object();
?
?? ? Singleton()
?? ? {
?? ? }
?
?? ?public?static?Singleton?Instance
?? ?{
?? ? ? ?get
?? ? ? ?{
?? ? ? ? ? ?if?(instance==null)
?? ? ? ? ? {
?? ? ? ? ? ? ? ?lock?(padlock)
?? ? ? ? ? ? ? ?{
?? ? ? ? ? ? ? ? ? ?if?(instance==null)
?? ? ? ? ? ? ? ? ? ?{
?? ? ? ? ? ? ? ? ? ? ? ?instance?=?new?Singleton();
?? ? ? ? ? ? ? ? ? ?}
?? ? ? ? ? ? ? ?}
?? ? ? ? ? ?}
?? ? ? ? ? ?return?instance;
?? ? ? ?}
?? ?}
}
這種實現方式對多線程來說是安全的,同時線程不是每次都加鎖,只有判斷對象實例沒有被創建時它才加鎖,有了我們上面第一部分的里面的分析,我們知道,加鎖后還得再進行對象是否已被創建的判斷。它解決了線程并發問題,同時避免在每個?Instance?屬性方法的調用中都出現獨占鎖定。它還允許您將實例化延遲到第一次訪問對象時發生。實際上,應用程序很少需要這種類型的實現。大多數情況下我們會用靜態初始化。這種方式仍然有很多缺點:無法實現延遲初始化。
4.靜態初始化
public?sealed?class?Singleton{
?? ?static?readonly?Singleton?instance=new?Singleton();
?? ?static?Singleton()
?? ?{
?? ?}
?? ?Singleton()
?? ?{
?? ?}
?? ?public?static?Singleton?Instance
?? ?{
?? ? ? ?get
?? ? ? ?{
?? ? ? ? ? ?return?instance;
?? ? ? ?}
?? ?}
}
看到上面這段富有戲劇性的代碼,我們可能會產生懷疑,這還是Singleton模式嗎?在此實現中,將在第一次引用類的任何成員時創建實例。公共語言運行庫負責處理變量初始化。該類標記為?sealed?以阻止發生派生,而派生可能會增加實例。此外,變量標記為?readonly,這意味著只能在靜態初始化期間(此處顯示的示例)或在類構造函數中分配變量。
該實現與前面的示例類似,不同之處在于它依賴公共語言運行庫來初始化變量。它仍然可以用來解決?Singleton?模式試圖解決的兩個基本問題:全局訪問和實例化控制。公共靜態屬性為訪問實例提供了一個全局訪問點。此外,由于構造函數是私有的,因此不能在類本身以外實例化?Singleton?類;因此,變量引用的是可以在系統中存在的唯一的實例。
由于?Singleton?實例被私有靜態成員變量引用,因此在類首次被對?Instance?屬性的調用所引用之前,不會發生實例化。
這種方法唯一的潛在缺點是,您對實例化機制的控制權較少。在?Design Patterns?形式中,您能夠在實例化之前使用非默認的構造函數或執行其他任務。由于在此解決方案中由?.NET Framework?負責執行初始化,因此您沒有這些選項。在大多數情況下,靜態初始化是在?.NET?中實現?Singleton?的首選方法。
5.延遲初始化
public?sealed?class?Singleton{
?? ?Singleton()
?? ?{
?? ?}
?? ?public?static?Singleton?Instance
?? ?{
?? ? ? ?get
?? ? ? ?{
?? ? ? ? ? ?return?Nested.instance;
?? ? ? ?}
?? ?}
?? ?
?? ?class?Nested
?? ?{
?? ? ? ?static?Nested()
?? ? ? ?{
?? ? ? ?}
?? ? ? ?internal?static?readonly?Singleton?instance?=?new?Singleton();
?? ?}
}
這里,初始化工作有Nested類的一個靜態成員來完成,這樣就實現了延遲初始化,并具有很多的優勢,是值得推薦的一種實
現方式。
實現要點
- ?? ? ? ?Singleton模式是限制而不是改進類的創建。
- ?? ? ? ? Singleton類中的實例構造器可以設置為Protected以允許子類派生。
- ?? ? ? ? Singleton模式一般不要支持Icloneable接口,因為這可能導致多個對象實例,與Singleton模式的初衷違背。
- ?? ? ? ? Singleton模式一般不要支持序列化,這也有可能導致多個對象實例,這也與Singleton模式的初衷違背。
- ?? ? ? ? Singleton只考慮了對象創建的管理,沒有考慮到銷毀的管理,就支持垃圾回收的平臺和對象的開銷來講,我們一般沒必要對其銷毀進行特殊的管理。
- ?? ? ? ? 理解和擴展Singleton模式的核心是“如何控制用戶使用new對一個類的構造器的任意調用”。
- ?? ? ? ? 可以很簡單的修改一個Singleton,使它有少數幾個實例,這樣做是允許的而且是有意義的。
優點
- ?? ? ? ? 實例控制:Singleton?會阻止其他對象實例化其自己的?Singleton?對象的副本,從而確保所有對象都訪問唯一實例
- ?? ? ? ? 靈活性:因為類控制了實例化過程,所以類可以更加靈活修改實例化過程
缺點
- ?? ? ? ? 開銷:雖然數量很少,但如果每次對象請求引用時都要檢查是否存在類的實例,將仍然需要一些開銷。可以通過使用靜態初始化解決此問題,上面的五種實現方式中已經說過了。
- ?? ? ? ? ?可能的開發混淆:使用?singleton?對象(尤其在類庫中定義的對象)時,開發人員必須記住自己不能使用?new?關鍵字實例化對象。因為可能無法訪問庫源代碼,因此應用程序開發人員可能會意外發現自己無法直接實例化此類。
- ?? ? ? ? 對象的生存期:Singleton?不能解決刪除單個對象的問題。在提供內存管理的語言中(例如基于?.NET Framework?的語言),只有?Singleton?類能夠導致實例被取消分配,因為它包含對該實例的私有引用。在某些語言中(如C++),其他類可以刪除?
對象實例,但這樣會導致?Singleton?類中出現懸浮引用。
適用性
- ?? ? ? ? 當類只能有一個實例而且客戶可以從一個眾所周知的訪問點訪問它時。
- ?? ? ? ? 當這個唯一實例應該是通過子類化可擴展的,并且客戶應該無需更改代碼就能使用一個擴展的實例時。
應用場景
- ?? ? ? ? 每臺計算機可以有若干個打印機,但只能有一個Printer Spooler,避免兩個打印作業同時輸出到打印機。?
(摘自呂震宇的C#設計模式(7)-Singleton Pattern)
- ?? ? ? ? PC機中可能有幾個串口,但只能有一個COM1口的實例。
- ?? ? ? ? 系統中只能有一個窗口管理器。
- ?? ? ? ? .NET Remoting中服務器激活對象中的Sigleton對象,確保所有的客戶程序的請求都只有一個實例來處理。
作者:TerryLee
出處:http://terrylee.cnblogs.com?
轉載于:https://www.cnblogs.com/tianyutingxy/archive/2010/10/23/1859177.html
總結
以上是生活随笔為你收集整理的单件模式(Singleton Pattern)(转自TerryLee)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: J2ME最佳实践之联网开发-编写反应灵敏
- 下一篇: 理论与实践