一、前言
在上一節(jié)中我們對設計模式進行了一定的介紹及分類。設計模式分為創(chuàng)建型、結構型、行為型。
創(chuàng)建型模式——主要負責對象的創(chuàng)建。結構型職責——主要負責處理類與對象的組合。行為型模式——主要負責類與對象交互中的職責的分配問題
。今天我們也是講述介紹創(chuàng)建型模式中的第一個模式——單例模式。
二、單例模式介紹
(一)來由
單例模式(Singleton Pattern)是最簡單的一個設計模式
,這種設計模式屬于創(chuàng)建型模式。在程序中總會有一些特殊的類。它們必須保證在系統(tǒng)中只存在一個實例,這個單一的類自己創(chuàng)建自己的對象,同時確保只有單個對象被創(chuàng)建,并且提供唯一的訪問形式??梢灾苯舆M行訪問,不用再新建實例。
那么如何避開常規(guī)的設計,來實現(xiàn)一個類一個實例、并且保證唯一調用呢?這時候就是單例模式施展身手的時候了。
?。ǘ┮鈭D
保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
?。ㄈ﹩卫J綄崿F(xiàn)方法
單例模式到底又是如何實現(xiàn)的呢?既然是單一實例,那么隊友多線程又該如何處理呢?下面我們一一來看看單例模式的實現(xiàn)。單例模式我們又涉及到其實現(xiàn)的多種形式——
非線程安全、線程安全、雙重驗證線程安全、不用鎖線程安全、完全延遲加載、使用.NET4的Lazy<T>類型。
1.?非線程安全
? ?
/// <summary> /// 非線程安全 /// </summary> public sealed class Singleton1 { ///
<summary> /// 定義靜態(tài)變量保存實例 /// </summary> public static Singleton1 Instance = null
;/// <summary> /// 定義私有構造函數(shù)保護,使其他地方不得實例 /// </summary> private Singleton1() { }
public string GetString() { return "非線程安全的單例模式"; } /// <summary> ///
定義公共方法,實現(xiàn)全局訪問/// </summary> /// <returns></returns> public static Singleton1
GetInstance() {//判斷實例狀態(tài) if (Instance==null) { Instance = new Singleton1(); }
return Instance; } } View Code
?
在上述事例中完美的實現(xiàn)了單線程的單例模式的情況。這里我們也需要注意一些的情況:
① 單例類包含一個private的私有構造函數(shù)
② 類申明sealed 密封不可繼承(不強制)
③ 類中有一個靜態(tài)變量保存實例
④ 類中提供有一個靜態(tài)方法或者屬性實現(xiàn)實例的創(chuàng)建引用全局調用訪問
⑤ 在多線程中單例模式需要另行處理,不然有可能得到類的多個實例
?
2.?線程安全
/// <summary> /// 線程安全單例模式 /// </summary> public sealed class Singleton2 { ///
<summary> /// 定義靜態(tài)變量保存實例 /// </summary> public static Singleton2 Instance = null
;private static readonly object locks=new object(); /// <summary> ///
定義私有構造函數(shù)保護,使其他地方不得實例/// </summary> private Singleton2() { } public string
GetString() {return "線程安全的單例模式"; } /// <summary> /// 定義公共方法,實現(xiàn)全局訪問 ///
</summary> /// <returns></returns> public static Singleton2 GetInstance() { //
對線程進行加鎖限制,掛起后來的線程。保證實例安全 lock (locks) { if (Instance == null) { Instance = new
Singleton2(); } }return Instance; } } View Code
?
3.?不用鎖線程安全
/// <summary> /// 不用鎖線程安全單例模式 /// </summary> public sealed class Singleton3 {
/// <summary> /// 定義靜態(tài)變量保存實例 /// </summary> private static readonly Singleton3
Instance =new Singleton3 (); static Singleton3() { } /// <summary> ///
定義私有構造函數(shù)保護,使其他地方不得實例/// </summary> private Singleton3() { } public string
GetString() {return "不用鎖線程安全單例模式"; } /// <summary> /// 定義公共方法,實現(xiàn)全局訪問 ///
</summary> /// <returns></returns> public static Singleton3 GetInstance() {
return Instance; } } View Code
?
這個實現(xiàn)方法沒有使用到鎖,但是也實現(xiàn)了線程安全。在第一次調用的時候會創(chuàng)建一個instance。這個實現(xiàn)也有一定的安全隱患。
*
*
* instance被創(chuàng)建的時機不明,任何對Singleton的調用都會提前創(chuàng)建instance
*
static構造函數(shù)的循環(huán)調用。如有A,B兩個類,A的靜態(tài)構造函數(shù)中調用了B,而B的靜態(tài)構造函數(shù)中又調用了A,這兩個就會形成一個循環(huán)調用,嚴重的會導致程序崩潰。
*
我們需要手動添加Singleton的靜態(tài)構造函數(shù)來確保Singleton類型不會被自動加上beforefieldinit這個Attribute,以此來確保instance會在第一次調用Singleton時才被創(chuàng)建。
*
readonly的屬性無法在運行時改變,如果我們需要在程序運行時dispose這個instance再重新創(chuàng)建一個新的instance,這種實現(xiàn)方法就無法滿足。
?
4.?完全延遲加載
/// <summary> /// 實現(xiàn)完全延遲加載單例模式 /// </summary> public sealed class Singleton4 {
/// <summary> /// 定義私有構造函數(shù)保護,使其他地方不得實例 /// </summary> private Singleton4() { }
/// <summary> /// 提供訪問位置 /// </summary> public static Singleton4 Instance { get
{return GetInstance.instance; } } /// <summary> /// 定義私有類確保第一次加載是初始化及調用 ///
</summary> private class GetInstance { static GetInstance(){} internal static
readonly Singleton4 instance = new Singleton4(); } public string GetString() {
return "實現(xiàn)完全延遲加載單例模式"; } } View Code
?
它確保了instance只會在Instance的get方法里面調用,且只會在第一次調用前初始化。是上一個版本的延遲加載的版本
5.?使用.NET4的Lazy<T>類型
?
/// <summary> /// 使用Lazy<T>實現(xiàn)完全延遲加載單例模式 /// </summary> public sealed class
Singleton5 {/// <summary> /// 延遲加載初始化 /// </summary> private static readonly
Lazy<Singleton5> lazy=new Lazy<Singleton5>(()=>new Singleton5()); /// <summary>
/// 定義私有構造函數(shù)保護,使其他地方不得實例 /// </summary> private Singleton5() { } /// <summary>
/// 提供全局訪問點 /// </summary> /// <returns></returns> public static Singleton5
Instance() {return lazy.Value; } public string GetString() { return "
實現(xiàn)完全延遲加載單例模式"; } } View Code
?
?
?
在.NET4.0中,可以使用Lazy<T>
來實現(xiàn)對象的延遲初始化,從而優(yōu)化系統(tǒng)的性能。延遲初始化就是將對象的初始化延遲到第一次使用該對象時。延遲初始化是我們在寫程序時經(jīng)常會遇到的情形,例如創(chuàng)建某一對象時需要花費很大的開銷,而這一對象在系統(tǒng)的運行過程中不一定會用到,這時就可以使用延遲初始化,在第一次使用該對象時再對其進行初始化,如果沒有用到則不需要進行初始化,這樣的話,使用延遲初始化就提高程序的效率,從而使程序占用更少的內存。
三、使用場合及優(yōu)缺點
一、使用場合
1、當類只需要且只能有一個實例并且需要全局訪問的時候。
2、當類是使用子類化擴展,并且無需更改代碼就可以使用擴展實例的情況下。
二、優(yōu)點
1、控制實例數(shù)量:保證實例數(shù)量的唯一且是全局訪問。
2、靈活性:類控制了實例化的全過程,這樣可以更加靈活的修改實例化過程
3、資源節(jié)?。罕苊鈱Y源的多重占用
三、缺點
1、沒有接口、也不能繼承。這個與單一責任原則相沖突,一個類只應該負責其邏輯,而不應該去負責如何實例。
四、總結
在設計模式的學習過程中,單例模式較為簡單,實現(xiàn)操作并不是特別難,但是在我們實例運用中也當注意下,比較如果使用出現(xiàn)問題。找到問題還是稍微困難的。這篇文章也介紹了幾種單例模式的使用方法,在我們使用時擇優(yōu)選取最佳方案。下一節(jié)我們將為全面講述二級野怪、并學習攻克它。
生命不息、戰(zhàn)斗不止!
C#設計模式系列目錄 <https://www.cnblogs.com/hulizhong/p/11394686.html>
歡迎大家掃描下方二維碼,和我一起踏上設計模式的闖關之路吧!
?
熱門工具 換一換