在什么情況下使用單例模式
使用單例模式的條件
使用單例模式有一個很重要的必要條件:
在一個系統(tǒng)要求一個類只有一個實例時才應(yīng)當使用單例模式。反過來說,如果一個類可以有幾個實例共存,那么就沒有必要使用單例類。但是有經(jīng)驗的讀者可能會看到很多不當?shù)厥褂脝卫J降睦樱梢娮龅缴厦孢@一點并不容易,下面就是一些這樣的情況。
例子一
問:我的一個系統(tǒng)需要一些"全程"變量。學習了單例模式后,我發(fā)現(xiàn)可以使用一個單例類盛放所有的"全程"變量。請問這樣做對嗎? 答:這樣做是違背單例模式的用意的。單例模式只應(yīng)當在有真正的"單一實例"的需求時才可使用。
一個設(shè)計得當?shù)南到y(tǒng)不應(yīng)當有所謂的"全程"變量,這些變量應(yīng)當放到它們所描述的實體所對應(yīng)的類中去。將這些變量從它們所描述的實體類中抽出來, 放到一個不相干的單例類中去,會使得這些變量產(chǎn)生錯誤的依賴關(guān)系和耦合關(guān)系。
例子二
問:我的一個系統(tǒng)需要管理與數(shù)據(jù)庫的連接。學習了單例模式后,我發(fā)現(xiàn)可以使用一個單例類包裝一個Connection 對象,并在finalize()方法中關(guān)閉這個Connection 對象。這樣的話,在這個單例類的實例沒有被人引用時,這個finalize() 對象就會被調(diào)用,因此,Connection 對象就會被釋放。這多妙啊。
答:這樣做是不恰當?shù)摹3怯袉我粚嵗男枨螅蝗徊灰褂脝卫J健T谶@里Connection 對象可以同時有幾個實例共存,不需要是單一實例。
單例模式有很多的錯誤使用案例都與此例子相似,它們都是試圖使用單例模式管理共享資源的生命周期,這是不恰當?shù)摹?BR> 單例類的狀態(tài)
有狀態(tài)的單例類
一個單例類可以是有狀態(tài)的(stateful),一個有狀態(tài)的單例對象一般也是可變(mutable) 單例對象。 有狀態(tài)的可變的單例對象常常當做狀態(tài)庫(repositary)使用。比如一個單例對象可以持有一個int 類型的屬性,用來給一個系統(tǒng)提供一個數(shù)值惟一的序列號碼,作為某個販賣系統(tǒng)的賬單號碼。當然,一個單例類可以持有一個聚集,從而允許存儲多個狀態(tài)。
沒有狀態(tài)的單例類
另一方面,單例類也可以是沒有狀態(tài)的(stateless), 僅用做提供工具性函數(shù)的對象。既然是為了提供工具性函數(shù),也就沒有必要創(chuàng)建多個實例,因此使用單例模式很合適。一個沒有狀態(tài)的單例類也就是不變(Immutable) 單例類; 關(guān)于不變模式,讀者可以參見本書的"不變(Immutable )模式"一章。
多個JVM 系統(tǒng)的分散式系統(tǒng)
EJB 容器有能力將一個EJB 的實例跨過幾個JVM 調(diào)用。由于單例對象不是EJB,因此,單例類局限于某一個JVM 中。換言之,如果EJB 在跨過JVM 后仍然需要引用同一個單例類的話,這個單例類就會在數(shù)個JVM 中被實例化,造成多個單例對象的實例出現(xiàn)。一個J2EE應(yīng)用系統(tǒng)可能分布在數(shù)個JVM 中,這時候不一定需要EJB 就能造成多個單例類的實例出現(xiàn)在不同JVM 中的情況。
如果這個單例類是沒有狀態(tài)的,那么就沒有問題。因為沒有狀態(tài)的對象是沒有區(qū)別的。但是如果這個單例類是有狀態(tài)的, 那么問題就來了。舉例來說,如果一個單例對象可以持有一個int 類型的屬性,用來給一個系統(tǒng)提供一個數(shù)值惟一的序列號碼,作為某個販賣系統(tǒng)的賬單號碼的話,用戶會看到同一個號碼出現(xiàn)好幾次。 在任何使用了EJB、RMI 和JINI 技術(shù)的分散式系統(tǒng)中,應(yīng)當避免使用有狀態(tài)的單例模式。
多個類加載器
同一個JVM 中會有多個類加載器,當兩個類加載器同時加載同一個類時,會出現(xiàn)兩個實例。在很多J2EE 服務(wù)器允許同一個服務(wù)器內(nèi)有幾個Servlet 引擎時,每一個引擎都有獨立的類加載器,經(jīng)有不同的類加載器加載的對象之間是絕緣的。
比如一個J2EE 系統(tǒng)所在的J2EE 服務(wù)器中有兩個Servlet 引擎:一個作為內(nèi)網(wǎng)給公司的網(wǎng)站管理人員使用;另一個給公司的外部客戶使用。兩者共享同一個數(shù)據(jù)庫,兩個系統(tǒng)都需要調(diào)用同一個單例類。如果這個單例類是有狀態(tài)的單例類的話,那么內(nèi)網(wǎng)和外網(wǎng)用戶看到的單例對象的狀態(tài)就會不同。除非系統(tǒng)有協(xié)調(diào)機制,不然在這種情況下應(yīng)當盡量避免使用有狀態(tài)的單例類。 |