2009-09-19 129 views
20

目前我對這種「設計模式」非常感興趣。我不確定,如果使用這種嚴格的全球狀態實施是否存在垮臺。那麼,你認爲什麼時候不要在應用程序中練習單身人士?Singleton Design Pattern:陷阱

+0

[這裏](https://www.michaelsafyan.com/tech/design/patterns/singleton)是一個很好的閱讀單身人士是反模式。 – RBT 2016-10-25 06:31:31

回答

35

如果你正在進行單元測試,那麼單例通常是一個壞主意,通常不會做單元測試(或BDD或驗收測試)。

使對象具有全局狀態意味着您編寫的涉及這些對象的單元測試將被隔離並相互分離。相反,你將不得不擔心重置每個測試的狀態,並相信我......這絕不是100%的時間。如果你不重置全局狀態,那麼你開始變得非常奇怪,很難在你的測試中調試錯誤,這會浪費時間。

全局狀態還會增加代碼中的耦合性,並使其很難重構。

理想的方法是使用IoC/DI容器(Spring,Guice等)來請求對象。這些容器通常具有使對象顯示爲「單例」的方法,但它們也可以根據情況(即單元測試與您的域代碼)來修改該行爲。

這一切都取決於你的問題的大小當然。如果你正在一起測試4級測試平臺來嘗試一些東西,那麼繼續使用Singleton。然而,只要該項目開始生活並越來越大,越來越複雜,那麼就會重構Singleton。

+0

所以你的意思是說,這些單身人士永遠不可能在實際項目中使用(並且在實際項目中發現單身人士自動等同於代碼氣味,而不管用例如何)? – Pacerier 2014-06-24 23:57:11

+0

我聞到一個巨魔...... – 2014-06-25 09:28:30

+0

然後不要嗅到自己。你的答案表明,單身人士只能用於「初始階段」的代碼,應該在所有越來越大的成熟項目中加以考慮。那麼你的意思是說,這些單身人士從來沒有資格在這些項目中使用,或者是否存在有效的用例? – Pacerier 2014-06-25 15:16:44

3

我會很少使用Singleton。由於其本質(靜態,全局對象),它們很難用於單元測試代碼。您最終需要在某些重新初始化機制中進行一些同步或構建,以便您可以爲每個單元測試獲得新版本。有些情況是有道理的 - 比如說,全球配置類 - 但它們比新來的單身人士似乎相信要少得多。我知道我經歷了一個階段,在那裏我看到了單點模式的應用。現在我儘可能避免它,並通過在代碼中重構來撤消它,因爲我遇到了不必要的實現。

7

除了其他文章中提到的測試和設計問題,Singleton和類加載器也存在問題。對於每個JVM或應用程序,單例並不是真正的「單一」 - 他們通過靜態屬性來完成這個任務,這意味着每個類都有一個。如果有多個類加載器 - 就像在大多數應用程序服務器中一樣 - 每個獨立的應用程序都會得到一個新的類加載器,甚至在EJB中使用多級加載器。每個類加載器都會加載一個單例實例 - 這取決於您對單例執行的操作,可能不會產生您期望的結果。

+1

@Nate,這聽起來很有趣,你能給我一個具體的例子嗎? – eric2323223 2009-09-19 16:36:38

+0

有沒有權威來源爲您的要求?爲什麼不分離JRE的實例使用獨立的單例實例? – Pacerier 2014-06-25 00:03:15

+0

@Pacerier http://www.oracle.com/technetwork/articles/java/singleton-1577166.html使用單獨的單例實例分離JRE是預期的情況 - 問題是單個JRE可以有多個類加載器,因此可以有多個單例實例。 – Nate 2014-06-25 13:08:29

18

Google技術會談前段時間有關於Global State and Singletons的良好演示。靜態單例模式是邪惡的,因爲它會導致不需要的副作用,並使代碼無法測試。靜態單例是全局變量的OO版本。

解決的辦法是到只需創建一個對象實例並通過依賴注入將其傳遞給它的用戶。 DI框架,例如Guice,可以很容易地定義好這種單身(在Guice中用@Singleton來註釋一個類)。還有一個類似的技術講座叫做Don't Look For Things!,它更多地討論了DI。