?????? 基本的 23 種設計模式從目的上可分為三種:
1、? 創建型( Creational )模式:負責對象創建。
2、? 結構型( Structural )模式:處理類與對象間的組合,可以解決一些繼承依賴性的問題
3、? 行為型( Behavioral )模式:類與對象交互中的職責分配,可以解決組件間如何和交互,隔離變化。
下面來說說單件模式:
首先說說單件模式產生的動機,也就是為什么會出現單件模式。有一些類在系統中只存在一個實例才能確保他們的邏輯正確性以及良好的效率。這時我想到我遇到的一個問題。我曾經遇到一個 WinForm 程序,運行后出現一個登陸框,輸入用戶名密碼后點擊登陸,然后顯示一個登陸后的界面。但是點擊登陸后,程序要做一些操作,由于這段操作用時相對較長,在不經意時,我有點擊了一次登陸按鈕,最后出現了兩個對話框。如:我現在有兩個 Form 窗體 Form1 和 Form2 。 Form1 上有一個按鈕用來打開 Form2 并隱藏自己。我們可以這樣寫:
????????
private
void button1_Click(object sender, System.EventArgs e)
??????? {
??????????? Form2 form = new Form2();
??????????? form.Show();
???????????
this.Hide();
??? }
如果我們在顯示Form2前由一些比較耗時的操作。如:我們讓線程的沉睡10秒在顯示Form2,當我們在線程沉睡時繼續點擊Form1上的Button,有可能就會出現兩個Form2的窗體。(我試過可以出現兩個Form2,如果你有心試但沒事出來別拿西紅柿砍我,哈哈)
???????
private
void button1_Click(object sender, System.EventArgs e)
??????? {
??????????? Thread.Sleep(10000);
??????????? Form2 form = new Form2();
??????????? form.Show();
???????????
this.Hide();
??? }
這種情況出現不能怪客戶多點了一下,也不能說是編譯器不夠智能,應該是我們程序上的Bug,我想這種情況用單件模式應該可以解決。
單件模式的使用意圖就是:保證一個類僅有一個實例,并提供一個該實例全局的訪問點(這句話當然不是我先說的,是引用Gof在《設計模式》中的一句話)
那類的設計者如何繞過常規的構造器來實現單件模式呢?下面就來談談單件模式的實現。
單件模式在結構上使用了景泰方法來約束構造器(也就是構造函數)創建對象。
在單線程的情況下:私有化構造函數,使類的使用者調用不到這個構造函數來new一個實例。類型中可以自己new一個實例。類中創建一個靜態私有變量和Static公有屬性。在公有屬性中實現此類的實例化。這樣在第一次請求時創建此對象。代碼如下:
class
Singleton
??? {
???????
private
static Singleton _instance;
???????
???????
private Singleton(){}
???????
public
static Singleton f_Instance
??????? {
???????????
get
??????????? {
???????????????
if(_instance == null)
??????????????? {
??????????????????? _instance = new Singleton();
??????????????? }
???????????????
return _instance;
??????????? }
??????? }
??? }
我在main函數中寫入如下程序來查看一下這樣寫是否有效:
static
void
??????? {
??????????? Singleton t1 = Singleton.f_Instance;
??????????? Singleton t2 = Singleton.f_Instance;
??????????? Console.Write(object.ReferenceEquals(t1,t2));
??????????? Console.Read();
??????? }
控制臺顯示為True,開來還是有效的。當然在Main中我也試過這樣寫:Singleton t1 = new Singleton(),編譯時告訴我Singleton()不可訪問(當然,人家是私有的,不是自家人當然不見)
這種單線程下的單件模式有幾點要注意:
1、?
構造器私有化(如果要此類被繼承,可以用protected聲明構造器)
2、?
不要支持IClinieable接口,因為會導致多個對象實例的出現
3、?
不能支持序列化
4、?
單件模式只考慮了對象創建的管理,沒有考慮對象的銷毀管理(創建自己的對象,銷毀的事交給垃圾回收器吧)
5、?
不能應對多線程環境,因為會導致多個對象實例的出現
那在多線程下如何實現呢?代碼如下:
class
SingletonMuli//
多線程Singleton模式
??? {
???????
private
static
volatile SingletonMuli _instance;??? //volatile
是為了讓編譯器對此代碼編譯后的位置不進行調整
???????
private SingletonMuli(){}
???????
private
static
object lockHelper = newobject();??? //
輔助器,不參與對象構建
???????
public
static SingletonMuli f_Instance
??????? {
???????????
get
??????????? {
???????????????
if(_instance == null)
??????????????? {
???????????????????
lock(lockHelper)
??????????????????? {
???????????????????????
if(_instance == null)?????? //
雙檢查
???
??????????????????? {
??????????????????????????? _instance = new SingletonMuli();
??????????????????????? }
??????????????????? }
??????????????? }
???????????????
return _instance;
??????????? }
??????? }
??? }
當然還有一些更簡單的實現方法,如:
???
class Singleton1//
可以用在多線程環境
??? {
???????
public
static
readonly Singleton1 _instance = new Singleton1();
???????
private Singleton1(){}
??? }
其中要提到的是在_instance私有字段的實例化叫做“內聯初始化”。內聯初始化是指在聲明時。
實際上面的代碼上相當于如下代碼:
Public static readonly Singleton1 _instance;
Static Singleton()? //
靜態構造函數
{
??? _instance = new Singleton();??????? //
私有構造器
}
Private Singleton(){}
內聯初始化時會先執行靜態構造器,如果沒有靜態構造函數,系統會默認一個。在訪問此靜態字段時執行靜態構造器生成。靜態構造器保證了在多線程時只有一個線程執行,自動加鎖。
當然,第二種實現方式也有一些缺點,如:靜態構造器必須是私有的、無參的。不過也可以用其他的方式解決這類問題。如可以用方法屬性實現擴展或修改私有構造器。
現在我們可以回來看看我開始說的那兩個Form的問題,我們現在可以這樣實現:
???????
private
static Form2 form;
???????
private
void button1_Click(object sender, System.EventArgs e)
??????? {
??????????? Thread.Sleep(10000);
???????
???
object lockhelp = newobject();
???????????
if(form == null)
??????????? {
???????????????
lock(lockhelp)
??????????????? {
???????????????????
if(form == null)
??????????????????? {
??????????????????????? form = new Form2();
??????????????????????? form.Show();
??????????????????? }
??????????????? }
??????????? }
???????????
this.Hide();
??????? }
這樣問題就解決了(我是沒有點出來第二個Form2,如果那位點出來了,給我發Email,我請她/他在天津的烤鴨)
單件模式實際上是利用控制對象創造過程來控制對象的創造個數的方法,我們可以對其進行擴展,不是讓他只生成一個對象,可以讓他只生成幾個對象,這樣可以實現對象池。
單件模式的核心是:如何控制用戶使用new對一個類的實例構造器的任意調用。