大話西遊之設計模式_從猴王出世看singleton
猴王出世
盤古開天闢地之後,天下分為四大部洲,在東勝神州傲來國的海邊上有一個花果山,此山風景秀麗,美不勝收,又多桃樹,遂成了獼猴的天堂。
花果山上有一塊仙石,此石自天地開始便吸收天地靈氣,慢慢周身生成360竅,開始呼吸,好像嬰兒一樣。終於有一天能量充滿,從石頭裡蹦出來個靈胎,見風化為一個石猴,出來之後,眼裡冒出兩道神光,先拜天地,再拜四方,驚動的天上的玉皇大帝,之後石猴喝了人間的河水,把靈氣壓住,成了一個普通的石猴。石猴性格灑脫、好動、很快和獼猴們打成一片,吃飯、玩耍、睡覺都在一起,過著快快樂樂的生活。
故事梗概
有一座山,山上面有一塊仙石,仙頭裡面有個靈胎,靈胎出世,變成石猴;
山上有很多桃樹和獼猴群。
石猴加入到了獼猴群。
程式碼模擬
由於整個模擬過程,程式碼較多,請點選 http://download.csdn.net/detail/myhc2014/9194253 下載,程式碼中更加清晰的瞭解猴王出世故事的流程及程式碼模擬,也能清楚的瞭解到singleton的全部內容,裡面還有其他模式哦。
關鍵程式碼
愛看西遊的人都知道,整個西遊中,一共有多少個孫悟空啊?
你的回答可能是這樣的:
只有一個,天上地下只有一個……
實際上,大家想表達的是只有一個孫悟空。
大家都知道,在物件導向的語言中,一切都是物件,想生成一個物件,只要通過構造方法new一下即可,
那麼,在程式碼中,如何保證只有一個孫悟空?
我是這麼做到的:
public class StoneMonkey extends Lingtai implements IMonkey {
/**
* 花果山仙石內的靈胎所變的石猴,天地間只有一個
*/
private static StoneMonkey stoneMonkey;
private StoneMonkey(List<LingQi> lingQis) {
super();
System.out.println("石猴孕育完成");
}
/**
* 單例
*
* @return
*/
public static synchronized StoneMonkey getStoneMonkey(List<LingQi> lingQis) {
if (stoneMonkey == null) {
stoneMonkey = new StoneMonkey(lingQis);
}
return stoneMonkey;
}
您可能感覺沒啥啊,這個看著沒什麼的程式碼,就能實現?
這個真的能實現,具體原因聽我道來(採用蘇格拉底誘導問答)。
問 | 答 |
---|---|
構造方法private,外面能new物件嗎? | 不能 |
外面不能new物件,那麼類內部能訪問private構造方法嗎? | 能,但是即使建立了物件,外面也無法訪問啊 |
如果通過物件呼叫的方式確實無法訪問,但是別忘了,還有可以通過類來呼叫的方法哦 | 哦,是的,靜態方法的確可以通過類直接進行訪問 |
那麼通過靜態方法,呼叫能否解決私有構造方法類物件的建立問題? | 恩,可以解決的 |
這樣就完了嗎? | 應該可以了 |
是的,在單執行緒的應用中是可以了,但在多執行緒併發下呢? | … |
synchronized/ lock 可以幫到你 |
好了,通過上面的對話式描述,對上文的程式碼是不是更清晰了?
其實啊,我用了一個非常簡單的模式來實現這個功能——singleton。
singleton? 聽著挺高階的,他到底是個什麼東西?
讓我們來一步一步的揭開他的神祕面紗
singleton
- 試用場景
當某個事情是獨一無二的,整個程式碼執行環境中,不會在出現第二個物件的時候(如天上地下就一個孫悟空),就可以考慮使用singleton。 - 程式碼特點
2.1. 構造方法私有
只有構造方法是私有的情況下,其他類才不能直接呼叫該類的構造方法來建立物件,此條件是單例的基礎。
2.2 開放靜態獲取類例項方法
在沒有該類物件的情況下,為了讓其他類能正常的獲取到該類的物件,所以需要寫一個public static 的方法,用於返回該類的物件/例項。
2.3 私有靜態物件例項
為了保證靜態方法能獲取到例項物件,所需需要一個靜態例項;
為了保證,外界只能通過開放的靜態同步方法獲取該例項,所以必須為private。 - 標準類圖
後續補上 - 標準實現程式碼
singleton共有兩種寫法,餓漢式 和 懶漢式 singleton,懶漢式還可以進化為 Double-checked Locking ,上文中的程式碼僅僅是懶漢式singleton。
4.1 懶漢式:
最常用的一種實現方案
public class StoneMonkey extends Lingtai implements IMonkey {
/**
* 花果山仙石內的靈胎所變的石猴,天地間只有一個
*/
private static StoneMonkey stoneMonkey;
private StoneMonkey(List<LingQi> lingQis) {
super();
System.out.println("石猴孕育完成");
}
/**
* 單例
*
* @return
*/
public static synchronized StoneMonkey getStoneMonkey(List<LingQi> lingQis) {
if (stoneMonkey == null) {
stoneMonkey = new StoneMonkey(lingQis);
}
return stoneMonkey;
}
有些朋友可能會問,為啥在getStoneMonkey前加了一個synchronized ?
因為在多執行緒的應用裡,不加 synchronized 可能會出現問題。
示例如下:
假設對 getStoneMonkey()方法的兩個呼叫幾乎同時發生,這時候會發生什麼?
1. 第一個執行緒檢測例項是否存在,因為例項不存在,該執行緒執行建立第一個例項的程式碼部分(new StoneMonkey(lingQis))。
2. 然而,假設在例項化完成之前,另一個執行緒也來檢測例項成員變數是否為null。因為第一個執行緒還什麼都沒有建立,例項成員變數仍然等於null,所以第二個執行緒也執行了建立一個物件的程式碼(new StoneMonkey(lingQis))。
3. 現在,兩個執行緒都執行了 singleton物件的new操作,因此建立了兩個物件。
影響可想而知(出現了多個孫悟空)!
為了解決這個問題,可以對整個獲取例項物件方法加鎖(synchronized)。
懶漢式構造物件特點:
在其他類呼叫“get例項物件方法”(如getStoneMonkey(List lingQis)) 的時候,在建立物件。
優點: 由於在需要的時候,才例項化物件,故在不需要使用物件的時候,無沒有必要的記憶體佔用(相對於餓漢式)。
缺點: 由於在需要的時候,才例項化物件,故獲“get例項物件方法”耗時長
4.2 Double-checked Locking
Double-checked Locking 只適用於多執行緒程式,如果在單執行緒的情況下,Double-checked Locking 是沒有意義的(根本就不需要考慮多執行緒問題)。
鑑於懶漢式的建立方法中,synchronized 鎖定了整個方法,而其他執行緒需要獲取該物件時,必須等待。這將導致一個瓶頸。
為了解決這個瓶頸問題,採用雙重校驗的方式進行相應的解決。
public class StoneMonkey2 extends Lingtai implements IMonkey {
/**
* 花果山仙石內的靈胎所變的石猴,天地間只有一個
*/
private static StoneMonkey2 stoneMonkey;
private StoneMonkey2(List<LingQi> lingQis) {
super();
System.out.println("石猴孕育完成");
}
/**
* 雙重檢測加鎖
*
* @return
*/
public static StoneMonkey2 getStoneMonkey(List<LingQi> lingQis) {
if (stoneMonkey == null) {
synchronized (StoneMonkey2.class) {
if (stoneMonkey == null) {
stoneMonkey = new StoneMonkey2(lingQis);
}
}
}
return stoneMonkey;
}
特點: 在建立物件前,新增一次檢查,避免不必要的鎖定
4.3 餓漢式
public class StoneMonkey3 extends Lingtai implements IMonkey {
/**
* 花果山仙石內的靈胎所變的石猴,天地間只有一個
*/
private static StoneMonkey3 stoneMonkey = new StoneMonkey3();;
private StoneMonkey3() {
super();
System.out.println("石猴孕育完成,沒有吸收靈氣");
}
/**
* 餓漢式
*
* @return
*/
public static StoneMonkey3 getStoneMonkey(List<LingQi> lingQis) {
return stoneMonkey;
}
特點:在類被虛擬機器載入時就自動呼叫構造方法(StoneMonkey3())生成例項物件了。
優點: 由於類被載入的時候,就建立了例項,故外面獲取例項物件時,耗時短。
缺點:由於類被載入的時候,就建立了例項,故可能會造成無用的記憶體佔用。
總結:
單例模式是非常非常簡單的一個模式,其兩種實現方式也是比較簡單的,至於平時的使用,建議大家多考慮一下場景,也多想一下孫悟空。
本人技術有限,如果有說不對的地方,歡迎隨時指正。
非常歡迎大家留言交流,讓我們共同進步~
相關文章
- 設計模式之——Singleton pattern設計模式
- 設計模式之單例模式 - Singleton設計模式單例
- 設計模式之Singleton - 單態模式設計模式
- Unity【話大】設計模式之狀態模式Unity設計模式
- 設計模式之單例模式(Singleton Pattern)設計模式單例
- Java設計模式之單例模式(Singleton)Java設計模式單例
- 大話設計模式設計模式
- Java設計模式之單例模式(Singleton Pattern)Java設計模式單例
- JAVA設計模式之 單例模式【Singleton Pattern】Java設計模式單例
- 設計模式(四)Singleton設計模式設計模式
- Singleton設計模式設計模式
- Java設計模式之從[反恐精英控制檯]分析單例(Singleton)模式Java設計模式單例
- 大話設計模式—命令模式設計模式
- 【大話設計模式】——代理模式設計模式
- 【大話設計模式】——策略模式設計模式
- 設計模式 - Singleton in Java設計模式Java
- 大話設計模式:工廠模式設計模式
- 【大話設計模式】—— 原型模式設計模式原型
- 【大話設計模式】——裝飾模式設計模式
- 【大話設計模式】—— 模板方法模式設計模式
- 大話設計模式—組合模式設計模式
- 大話設計模式—橋接模式設計模式橋接
- 設計模式之“物件效能模式”: Singleton 單例模式(筆記)設計模式物件單例筆記
- 設計模式—singleton(單例模式)設計模式單例
- 設計模式 - 單例模式(Singleton)設計模式單例
- 【大話設計模式】——淺談設計模式基礎設計模式
- 大話設計模式:抽象工廠模式設計模式抽象
- 【大話設計模式】—— 工廠方法模式設計模式
- 大話設計模式—備忘錄模式設計模式
- 【設計模式】設計模式(一)-- 大話設計模式讀書筆記設計模式筆記
- Singleton 單例設計模式單例設計模式
- java設計模式-單例模式SingletonJava設計模式單例
- 設計模式——3單例模式(Singleton)設計模式單例
- 大話設計模式:簡單工廠模式設計模式
- 【大話設計模式】——簡單工廠模式設計模式
- 大話 PHP 設計模式--建立型PHP設計模式
- 大話設計模式C++.pdf設計模式C++
- 大話設計模式:今天你設計了嗎?設計模式