設(shè)計(jì)模式之單例模式

從這篇開(kāi)始我的設(shè)計(jì)模式學(xué)習(xí)之旅涩维,以前面對(duì)設(shè)計(jì)模式總覺(jué)得是在看天書(shū)。果然袁波,生活就是最好的老師瓦阐,當(dāng)在社會(huì)上摸爬滾打過(guò)一番之后,別管多久篷牌,都會(huì)多少有點(diǎn)收獲睡蟋,這恐怕就是生活的意義吧。閑話(huà)不多說(shuō)枷颊,馬上入正題:在入正題之前再啰嗦一句戳杀,由于本人最熟悉的是java,所以所有的模式幾乎都是以java為例的夭苗。好P趴ā!馬上正題:

單例模式

這個(gè)模式估計(jì)是所有的入坑選手第一個(gè)接觸到的模式题造,也是聽(tīng)到最多的一個(gè)模式傍菇,對(duì)于每一個(gè)模式幾乎都是三步走:是什么(該模式的定義),為什么(使用該模式的原因)以及怎么做(如何去使用界赔,什么時(shí)候使用該模式)丢习。所以先來(lái)第一步:

單例模式?什么東西淮悼?

什么叫模式咐低?你可以通俗的理解為“套路”,那什么是單例袜腥?也就是“單一實(shí)例”见擦。學(xué)java的都知道,一個(gè)類(lèi)(class),是可以創(chuàng)建(new)出來(lái)很多對(duì)象(也就是實(shí)例)的鲤屡。比如:

public class Person{
    String name;
    public Person(String name){
        this.name = name;
    }
}

這是一個(gè)“人”類(lèi)儡湾,通過(guò)這個(gè)類(lèi)你可以創(chuàng)建很多人,什么張三执俩,什么李四啊徐钠,阿貓阿狗啊等等:

Person zhangsan = new Person("章三");
Person lisi = new Person("李四");

這樣對(duì)于Person這個(gè)類(lèi)來(lái)說(shuō)就不是單一實(shí)例了。<ber>
所以單例模式就是:有沒(méi)有一種套路可以讓我一個(gè)類(lèi)只創(chuàng)建出來(lái)一個(gè)實(shí)例役首?
可是

我們?yōu)槭裁匆獑卫J侥兀?/h3>

想象一下尝丐,現(xiàn)實(shí)中有沒(méi)有什么是只有一個(gè)的,當(dāng)然哦衡奥,不是說(shuō)全世界只有一個(gè)爹袁,而是在一個(gè)“系統(tǒng)”中只有一個(gè),比如說(shuō)在一部電腦里面電腦系統(tǒng)只有一個(gè)任務(wù)管理器矮固,再比如說(shuō)失息,在金庸先生的武俠世界里只有一個(gè)武林盟主。等等等等档址№锞ぃ可以看的出來(lái),“單一”的一個(gè)好處就是:方便管理守伸。我再舉一個(gè)代碼的例子吧:
現(xiàn)在有一個(gè)放鑰匙的鑰匙包:

public class KeyCase{
   private Map<String,Key> cases = new HashMap();
    //code ....
}

public class Key(
    public int password;
)

現(xiàn)在是這樣的绎秒,有一個(gè)需要用到鑰匙的游戲,在每一關(guān)尼摹,你都可以得到一個(gè)鑰匙包和一把鑰匙见芹。
現(xiàn)在來(lái)到了第一到關(guān)卡(第一個(gè)Activity),得到一條鑰匙(Key實(shí)例)蠢涝,然后開(kāi)了門(mén)玄呛,然后就很自然的將鑰匙放到了鑰匙包(KeyCase實(shí)例)里,等你準(zhǔn)備過(guò)去第二關(guān)(第二個(gè)activity)的時(shí)候和二,你發(fā)現(xiàn)鑰匙包壓根帶不過(guò)去(在android里徘铝,普通類(lèi)是沒(méi)辦法從一個(gè)Activity帶到另外一個(gè)activity的,只有可以序列化的類(lèi)才可以)儿咱,那沒(méi)辦法了庭砍,只能把鑰匙包放下场晶,先過(guò)去第二關(guān)看看了混埠。等來(lái)到第二關(guān)(跳轉(zhuǎn)到第二個(gè)Activity)的時(shí)候才發(fā)現(xiàn),媽呀诗轻,上一關(guān)的鑰匙居然還有用钳宪,可你現(xiàn)在會(huì)發(fā)現(xiàn),如果你現(xiàn)在再獲取到鑰匙包這個(gè)對(duì)象的時(shí)候,里面卻是空空如也吏颖,因?yàn)楝F(xiàn)在的鑰匙包已經(jīng)不是第一關(guān)的鑰匙包了搔体。如果這里的鑰匙包以單例模式創(chuàng)建那就不一樣了,因?yàn)樵谡麄€(gè)游戲下來(lái)半醉,都只有一個(gè)鑰匙包疚俱。這才更符合設(shè)定,畢竟誰(shuí)閑來(lái)無(wú)事帶幾十個(gè)鑰匙包出門(mén)(無(wú)奈程度基本跟用六位數(shù)密碼去保管兩位數(shù)的錢(qián)...無(wú)異)缩多〈艮龋總的來(lái)說(shuō),單例模式就是在一個(gè)系統(tǒng)中某些需要統(tǒng)一的地方衬吆,最好的方法就是讓他唯一再通俗點(diǎn)梁钾,就是當(dāng)你希望“這個(gè)類(lèi)只有一個(gè)就好了”的時(shí)候就是使用單例模式的時(shí)候。
這樣看來(lái)在某些方面,某些時(shí)候要是能保證單例也是不錯(cuò)的逊抡。

單例模式怎么搞姆泻?

首先控制實(shí)例的個(gè)數(shù)就是單例最核心的部分了,那么就不能讓外界去創(chuàng)建了冒嫡,因?yàn)槟鞘遣豢煽氐哪床缘谝稽c(diǎn)就是這個(gè)類(lèi)不能被外界new。如何做到不被外界new呢孝凌?將構(gòu)造方法私有化就好了:

public class KeyCase{
   private KeyCase(){}
    //code ....
}

好了潜秋,現(xiàn)在外界誰(shuí)都不能創(chuàng)建鑰匙包KeyCase了,問(wèn)題又來(lái)了胎许,那外界該怎么樣獲得這個(gè)鑰匙包實(shí)例熬骸?

別人不能辜窑,但是自己(鑰匙包)可以創(chuàng)建啊钩述。

public class KeyCase{
    private KeyCase keyCase = new KeyCase();
   private KeyCase(){}
   public KeyCase getInstance(){
       return keyCase;
   }
    //code ....
}

但是外界還是拿不到,只有鑰匙包的實(shí)例才能通過(guò)getInstance()方法拿到keyCase這個(gè)實(shí)例穆碎,可是目前也就只有這么一個(gè)實(shí)例牙勘,這樣就陷入了一個(gè)死循環(huán):要拿到KeyCase的實(shí)例首先要有它的實(shí)例。很尷尬K鳌方面!但是別怕,相信大家都見(jiàn)過(guò)這兩個(gè)關(guān)鍵字了色徘,staticfinal,一個(gè)static就解決了上面的問(wèn)題恭金,而final保證了不可變,所以代碼最終就變成了這樣:

public class KeyCase{
    private static final KeyCase keyCase = new KeyCase();//準(zhǔn)備好一個(gè)鑰匙包褂策,永遠(yuǎn)不變的鑰匙包
   private KeyCase(){}
   public static KeyCase getInstance(){
       return case;//我現(xiàn)在想用了就可以直接拿到横腿,真方便
   }
    //code ....
}

這就是單例模式里面的“餓漢式”颓屑,餓漢式將鑰匙包的對(duì)象keyCase當(dāng)作食物,餓漢首先準(zhǔn)備好食物耿焊,等餓的時(shí)候(需要的時(shí)候)就可以直接拿來(lái)吃(用)揪惦。

懶漢式

懶漢式是單例的另一種“套路”,而懶漢式的意思就是我很懶罗侯,我不想像餓漢那樣先準(zhǔn)備好器腋,我想等到我餓的時(shí)候才去準(zhǔn)備。喲呵钩杰!夠懶的蒂培。這樣我們就餓漢的代碼直接根據(jù)意思改:

//餓漢式
public class KeyCase{
    private static KeyCase keyCase ;//我先不準(zhǔn)備
   private KeyCase(){}
   public static KeyCase getInstance(){
       if(keyCase == null){//我現(xiàn)在餓了榜苫,但是我想要的還沒(méi)有  
           keyCase = new KeyCase();//那好吧护戳,只能先去準(zhǔn)備了  
       }
       return keyCase;//準(zhǔn)備好了,開(kāi)吃垂睬! 3
   }
    //code ....
}

好了媳荒,餓漢和懶漢兩種方式都實(shí)現(xiàn)了。但是驹饺,有缺陷哦钳枕!

目前的餓漢和懶漢的缺陷。

先說(shuō)餓漢吧赏壹。

只能說(shuō)是個(gè)小缺陷鱼炒,準(zhǔn)確點(diǎn)講,也不算是缺陷蝌借,可以算是餓漢式的特點(diǎn)昔瞧,那就是先準(zhǔn)備,如果準(zhǔn)備了不用菩佑,那就浪費(fèi)了一點(diǎn)空間自晰,餓漢式就是用空間來(lái)?yè)Q取時(shí)間,先創(chuàng)建而消耗了部分空間去換取使用時(shí)候的時(shí)間稍坯,所以餓漢式在類(lèi)加載的時(shí)候會(huì)比懶漢式要慢酬荞,但是在運(yùn)行的時(shí)候會(huì)比懶漢式要快。而且餓漢還是線(xiàn)程安全的瞧哟。沒(méi)意思(餓漢式?jīng)]什么缺陷可說(shuō)當(dāng)然沒(méi)意思)混巧,還是來(lái)看看懶漢式吧。

線(xiàn)程不安全的懶漢式

上面說(shuō)到餓漢式是空間換時(shí)間勤揩,而懶漢式就是時(shí)間換空間咧党,這樣就有一個(gè)問(wèn)題,那就是線(xiàn)程不安全雄可。如果現(xiàn)在有兩條線(xiàn)程執(zhí)行鑰匙包的getInstance方法凿傅,就有可能得到兩個(gè)對(duì)象,因?yàn)椴荒鼙WC在同一時(shí)間只有一條線(xiàn)程執(zhí)行到case=new KeyCase();這樣代碼数苫。

小E:“我知道我知道聪舒,加個(gè)同步就好了”!(虐急?箱残??這位小E同志是哪冒出來(lái)的止吁?)

public class KeyCase{
    private static KeyCase keyCase 被辑;
    private KeyCase(){}
    public static KeyCase getInstance(){
       if(keyCase == null){//1
        synchorinzed(KeyCase.class){
           keyCase = new KeyCase();//2          
           }
       }
       return keyCase;
   }
    //code ....
}

我:“不夠”!
照著上面的例子敬惦,照樣還是會(huì)有兩個(gè)線(xiàn)程停留在1處盼理,也就是都已經(jīng)判斷過(guò)了,就算排著隊(duì)去執(zhí)行2處的代碼俄删,還是會(huì)有兩個(gè)實(shí)例宏怔。
小E:“現(xiàn)在我是真的知道了,再加個(gè)判斷”:

public class KeyCase{
    private static KeyCase keyCase 畴椰;
    private KeyCase(){}
    public static KeyCase getInstance(){
       if(keyCase == null){//1
          synchorinzed(KeyCase.class){
            if(keyCase==null){//3
                keyCase = new KeyCase();//2
            }
           }
       }
       return keyCase;
   }
    //code ....
}

我:“很接近了臊诊,但還是不夠!”斜脂。是的抓艳,我也很長(zhǎng)一段時(shí)間認(rèn)為上面的是最安全的懶漢式了,然而看了一些大佬的文章我才知道帚戳,這樣還是不夠玷或,缺陷是第一條線(xiàn)程已經(jīng)執(zhí)行完了2處的代碼,生成了鑰匙包的實(shí)例片任,當(dāng)?shù)诙l線(xiàn)程來(lái)到3處是依舊有可能得到的信息是keyCase==null庐椒,也就是第二條線(xiàn)程不能及時(shí)的知道,keyCase已經(jīng)被第一條線(xiàn)程創(chuàng)建好了蚂踊。說(shuō)到這里就說(shuō)到j(luò)ava的內(nèi)存模型了约谈。但是這個(gè)在這就不細(xì)說(shuō)了,大家知道有這么一種可能就是了犁钟。有興趣的可以看一些有關(guān)volatile關(guān)鍵字的文章棱诱。這就是解決懶漢式最后一道線(xiàn)程缺陷的關(guān)鍵字,volatile保證了可見(jiàn)性涝动,什么是可見(jiàn)性呢迈勋,也就是volatile關(guān)鍵字修飾的變量對(duì)于線(xiàn)程們(多個(gè)線(xiàn)程)來(lái)說(shuō),無(wú)論是誰(shuí)修改了這個(gè)變量醋粟,其他線(xiàn)程對(duì)這個(gè)行為是可見(jiàn)的靡菇,就立馬知道重归。原來(lái)A線(xiàn)程這個(gè)小子偷偷修改了這個(gè)變量。還以為我不知道厦凤?我可是偷偷看著呢,不行鼻吮,先記在本子上!较鼓!

線(xiàn)程最安全的懶漢式

所以最后線(xiàn)程安全的懶漢式應(yīng)該是這樣的:

public class KeyCase{
    private static volatile KeyCase keyCase 椎木;
    private KeyCase(){}
    public static KeyCase getInstance(){
       if(keyCase == null){//1
            synchorinzed(KeyCase.class){
                if(keyCase==null){//3
                    keyCase = new KeyCase();//2
                }
            }
        }
        return keyCase;
    }
    //code ....
}

好了,單例模式最經(jīng)典的兩種已經(jīng)說(shuō)完了博烂,估計(jì)對(duì)單例模式還不了解的應(yīng)該印象深一點(diǎn)了吧香椎,有加深一點(diǎn)點(diǎn),對(duì)于我來(lái)說(shuō)就已經(jīng)足夠了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末禽篱,一起剝皮案震驚了整個(gè)濱河市畜伐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌躺率,老刑警劉巖烤礁,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異肥照,居然都是意外死亡脚仔,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)舆绎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鲤脏,“玉大人,你說(shuō)我怎么就攤上這事吕朵×源迹” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵努溃,是天一觀的道長(zhǎng)硫嘶。 經(jīng)常有香客問(wèn)我,道長(zhǎng)梧税,這世上最難降的妖魔是什么沦疾? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮第队,結(jié)果婚禮上哮塞,老公的妹妹穿的比我還像新娘。我一直安慰自己凳谦,他們只是感情好忆畅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著尸执,像睡著了一般家凯。 火紅的嫁衣襯著肌膚如雪缓醋。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,182評(píng)論 1 299
  • 那天绊诲,我揣著相機(jī)與錄音送粱,去河邊找鬼。 笑死驯镊,一個(gè)胖子當(dāng)著我的面吹牛葫督,可吹牛的內(nèi)容都是我干的竭鞍。 我是一名探鬼主播板惑,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼偎快!你這毒婦竟也來(lái)了冯乘?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤晒夹,失蹤者是張志新(化名)和其女友劉穎裆馒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體丐怯,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喷好,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了读跷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梗搅。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖效览,靈堂內(nèi)的尸體忽然破棺而出无切,到底是詐尸還是另有隱情,我是刑警寧澤丐枉,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布哆键,位于F島的核電站,受9級(jí)特大地震影響瘦锹,放射性物質(zhì)發(fā)生泄漏籍嘹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一弯院、第九天 我趴在偏房一處隱蔽的房頂上張望噩峦。 院中可真熱鬧,春花似錦抽兆、人聲如沸识补。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)凭涂。三九已至祝辣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間切油,已是汗流浹背蝙斜。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留澎胡,地道東北人孕荠。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像攻谁,于是被迫代替她去往敵國(guó)和親稚伍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容