——探索設計模式系列之二
Terrylee,2005年12月07日
概述
Sigleton模式要求一個(gè)類(lèi)有且僅有一個(gè)實(shí)例,并且提供了一個(gè)全局的訪(fǎng)問(wèn)點(diǎn)。這就提出了一個(gè)問(wèn)題:如何繞過(guò)常規的構造器,提供一種機制來(lái)保證一個(gè)類(lèi)只有一個(gè)實(shí)例?客戶(hù)程序在調用某一個(gè)類(lèi)時(shí),它是不會(huì )考慮這個(gè)類(lèi)是否只能有一個(gè)實(shí)例等問(wèn)題的,所以,這應該是類(lèi)設計者的責任,而不是類(lèi)使用者的責任。
從另一個(gè)角度來(lái)說(shuō),Sigleton模式其實(shí)也是一種職責型模式。因為我們創(chuàng )建了一個(gè)對象,這個(gè)對象扮演了獨一無(wú)二的角色,在這個(gè)單獨的對象實(shí)例中,它集中了它所屬類(lèi)的所有權力,同時(shí)它也肩負了行使這種權力的職責!
意圖
保證一個(gè)類(lèi)僅有一個(gè)實(shí)例,并提供一個(gè)訪(fǎng)問(wèn)它的全局訪(fǎng)問(wèn)點(diǎn)。
模型圖
邏輯模型圖:

物理模型圖:
生活中的例子
美國總統的職位是Sigleton,美國憲法規定了總統的選舉,任期以及繼任的順序。這樣,在任何時(shí)刻只能由一個(gè)現任的總統。無(wú)論現任總統的身份為何,其頭銜"美利堅合眾國總統"是訪(fǎng)問(wèn)這個(gè)職位的人的一個(gè)全局的訪(fǎng)問(wèn)點(diǎn)。

五種實(shí)現
1.簡(jiǎn)單實(shí)現
這種方式的實(shí)現對于線(xiàn)程來(lái)說(shuō)并不是安全的,因為在多線(xiàn)程的環(huán)境下有可能得到Sigleton類(lèi)的多個(gè)實(shí)例。如果同時(shí)有兩個(gè)線(xiàn)程去判斷(instance == null),并且得到的結果為真,這時(shí)兩個(gè)線(xiàn)程都會(huì )創(chuàng )建類(lèi)Sigleton的實(shí)例,這樣就違背了Sigleton模式的原則。實(shí)際上在上述代碼中,有可能在計算出表達式的值之前,對象實(shí)例已經(jīng)被創(chuàng )建,但是內存模型并不能保證對象實(shí)例在第二個(gè)線(xiàn)程創(chuàng )建之前被發(fā)現。
2


3
4
5
6


7
8
9
10


11
12


13
14


15
16
17
18
19
20
該實(shí)現方式主要有兩個(gè)優(yōu)點(diǎn):
l 由于實(shí)例是在 Instance 屬性方法內部創(chuàng )建的,因此類(lèi)可以使用附加功能(例如,對子類(lèi)進(jìn)行實(shí)例化),即使它可能引入不想要的依賴(lài)性。
l 直到對象要求產(chǎn)生一個(gè)實(shí)例才執行實(shí)例化;這種方法稱(chēng)為“惰性實(shí)例化”。惰性實(shí)例化避免了在應用程序啟動(dòng)時(shí)實(shí)例化不必要的 singleton。
2.安全的線(xiàn)程
2


3
4
5
6
7


8
9
10
11


12
13


14
15


16
17


18
19
20
21
22
23
24
25
26
這種方式的實(shí)現對于線(xiàn)程來(lái)說(shuō)是安全的。我們首先創(chuàng )建了一個(gè)進(jìn)程輔助對象,線(xiàn)程在進(jìn)入時(shí)先對輔助對象加鎖然后再檢測對象是否被創(chuàng )建,這樣可以確保只有一個(gè)實(shí)例被創(chuàng )建,因為在同一個(gè)時(shí)刻加了鎖的那部分程序只有一個(gè)線(xiàn)程可以進(jìn)入。這種情況下,對象實(shí)例由最先進(jìn)入的那個(gè)線(xiàn)程創(chuàng )建,后來(lái)的線(xiàn)程在進(jìn)入時(shí)(instence == null)為假,不會(huì )再去創(chuàng )建對象實(shí)例了。但是這種實(shí)現方式增加了額外的開(kāi)銷(xiāo),損失了性能。
3.雙重鎖定
2


3
4
5
6
7


8
9
10
11


12
13


14
15


16
17


18
19


20
21
22
23
24
25
26
27
28
4.靜態(tài)初始化
2


3
4
5
6


7
8
9
10


11
12
13
14


15
16


17
18
19
20
21
該實(shí)現與前面的示例類(lèi)似,不同之處在于它依賴(lài)公共語(yǔ)言運行庫來(lái)初始化變量。它仍然可以用來(lái)解決 Singleton 模式試圖解決的兩個(gè)基本問(wèn)題:全局訪(fǎng)問(wèn)和實(shí)例化控制。公共靜態(tài)屬性為訪(fǎng)問(wèn)實(shí)例提供了一個(gè)全局訪(fǎng)問(wèn)點(diǎn)。此外,由于構造函數是私有的,因此不能在類(lèi)本身以外實(shí)例化 Singleton 類(lèi);因此,變量引用的是可以在系統中存在的唯一的實(shí)例。
由于 Singleton 實(shí)例被私有靜態(tài)成員變量引用,因此在類(lèi)首次被對 Instance 屬性的調用所引用之前,不會(huì )發(fā)生實(shí)例化。
這種方法唯一的潛在缺點(diǎn)是,您對實(shí)例化機制的控制權較少。在 Design Patterns 形式中,您能夠在實(shí)例化之前使用非默認的構造函數或執行其他任務(wù)。由于在此解決方案中由 .NET Framework 負責執行初始化,因此您沒(méi)有這些選項。在大多數情況下,靜態(tài)初始化是在 .NET 中實(shí)現 Singleton 的首選方法。
5.延遲初始化
2


3
4


5
6
7
8


9
10


11
12
13
14
15
16


17
18


19
20
21
22
23
24
現方式。
實(shí)現要點(diǎn)
l Sigleton模式是限制而不是改進(jìn)類(lèi)的創(chuàng )建。 l Sigleton類(lèi)中的實(shí)例構造器可以設置為Protected以允許子類(lèi)派生。 l Sigleton模式一般不要支持Icloneable接口,因為這可能導致多個(gè)對象實(shí)例,與Sigleton模式的初衷違背。 l Sigleton模式一般不要支持序列化,這也有可能導致多個(gè)對象實(shí)例,這也與Sigleton模式的初衷違背。 l Sigleton只考慮了對象創(chuàng )建的管理,沒(méi)有考慮到銷(xiāo)毀的管理,就支持垃圾回收的平臺和對象的開(kāi)銷(xiāo)來(lái)講,我們一般沒(méi)必要對其銷(xiāo)毀進(jìn)行特殊的管理。 l 理解和擴展Sigleton模式的核心是“如何控制用戶(hù)使用new對一個(gè)類(lèi)的構造器的任意調用”。
l 可以很簡(jiǎn)單的修改一個(gè)Sigleton,使它有少數幾個(gè)實(shí)例,這樣做是允許的而且是有意義的。
優(yōu)點(diǎn)
l 實(shí)例控制:Singleton 會(huì )阻止其他對象實(shí)例化其自己的 Singleton 對象的副本,從而確保所有對象都訪(fǎng)問(wèn)唯一實(shí)例
l 靈活性:因為類(lèi)控制了實(shí)例化過(guò)程,所以類(lèi)可以更加靈活修改實(shí)例化過(guò)程
缺點(diǎn)
l 開(kāi)銷(xiāo):雖然數量很少,但如果每次對象請求引用時(shí)都要檢查是否存在類(lèi)的實(shí)例,將仍然需要一些開(kāi)銷(xiāo)??梢酝ㄟ^(guò)使用靜態(tài)初始化解決此問(wèn)題,上面的五種實(shí)現方式中已經(jīng)說(shuō)過(guò)了。
l 可能的開(kāi)發(fā)混淆:使用 singleton 對象(尤其在類(lèi)庫中定義的對象)時(shí),開(kāi)發(fā)人員必須記住自己不能使用 new 關(guān)鍵字實(shí)例化對象。因為可能無(wú)法訪(fǎng)問(wèn)庫源代碼,因此應用程序開(kāi)發(fā)人員可能會(huì )意外發(fā)現自己無(wú)法直接實(shí)例化此類(lèi)。
l 對象的生存期:Singleton 不能解決刪除單個(gè)對象的問(wèn)題。在提供內存管理的語(yǔ)言中(例如基于 .NET Framework 的語(yǔ)言),只有 Singleton 類(lèi)能夠導致實(shí)例被取消分配,因為它包含對該實(shí)例的私有引用。在某些語(yǔ)言中(如 C++),其他類(lèi)可以刪除
對象實(shí)例,但這樣會(huì )導致 Singleton 類(lèi)中出現懸浮引用。
適用性
l 當類(lèi)只能有一個(gè)實(shí)例而且客戶(hù)可以從一個(gè)眾所周知的訪(fǎng)問(wèn)點(diǎn)訪(fǎng)問(wèn)它時(shí)。
l 當這個(gè)唯一實(shí)例應該是通過(guò)子類(lèi)化可擴展的,并且客戶(hù)應該無(wú)需更改代碼就能使用一個(gè)擴展的實(shí)例時(shí)。
應用場(chǎng)景
l 每臺計算機可以有若干個(gè)打印機,但只能有一個(gè)Printer Spooler,避免兩個(gè)打印作業(yè)同時(shí)輸出到打印機。
(摘自呂震宇的C#設計模式(7)-Singleton Pattern)
l PC機中可能有幾個(gè)串口,但只能有一個(gè)COM1口的實(shí)例。
l 系統中只能有一個(gè)窗口管理器。
l .NET Remoting中服務(wù)器激活對象中的Sigleton對象,確保所有的客戶(hù)程序的請求都只有一個(gè)實(shí)例來(lái)處理。
完整示例
這是一個(gè)簡(jiǎn)單的計數器例子,四個(gè)線(xiàn)程同時(shí)進(jìn)行計數。

2
3
4
5


6

7
8
9
10
11
12


13

14
15
16

17
18
19
20
21


22

23
24
25
26
27
28


29
30
31
32
33
34

35
36


37
38
39
40

41
42


43
44
45
46
47
48

2
3
4
5
6


7

8
9
10
11
12
13


14
15


16
17
18
19

20
21
22
23


24

25
26
27

28
29
30

31
32


33

34
35
36
37
38
39
40
41
42
43
44

45
46
47
48
49
50


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

75
76
77
78
79

2
3
4
5
6


7

8
9
10
11
12
13


14
15


16
17
18
19
20
21
22
23
24
總結
Sigleton設計模式是一個(gè)非常有用的機制,可用于在面向對象的應用程序中提供單個(gè)訪(fǎng)問(wèn)點(diǎn)。文中通過(guò)五種實(shí)現方式的比較和一個(gè)完整的示例,完成了對Sigleton模式的一個(gè)總結和探索。用一句廣告詞來(lái)概括Sigleton模式就是“簡(jiǎn)約而不簡(jiǎn)單”。
_________________________________________________________________________________________________
源碼下載:/Files/Terrylee/SigletonPattern.rar
參考文獻:
《C#計模式》,中國電力出版社
使用 Microsoft .NET 的企業(yè)解決方案模式
《Implementing the Singleton Pattern in C#》
MSDN《Exploring the Singleton Design Pattern》
聯(lián)系客服