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