[英]Monostate vs. Singleton
什么情況下會使用單態模式而不是單例來維護全局對象?
編輯:我知道單例模式和單態模式是什么。 在很多場景中也實現了 Singleton。 只想知道需要實現 MonoState 模式的場景(案例)。
例如。 我需要在我的 Windows 窗體應用程序中維護每個屏幕的列列表。 在這種情況下,我可以使用單例字典。 但是,我將 List 存儲在靜態全局變量中,並且我想提供索引器(因為如果鍵不存在,我需要動態地將新條目添加到列表中),我可以將 ScreenDetails.ScreenName 指定為鍵並獲取 ScreenDetails .ColumnsTable。 由於索引器無法對靜態類進行操作,因此我將模式更改為 Monostate。
所以我想知道哪些其他場景可能會迫使用戶使用 Monostate 而不是 Singletons。
monostate 和 singleton 是同一個獎牌的兩個面(全局狀態):
IE:
Singleton singleton = Singleton.getInstance();
MonoState m1 = new MonoState(); MonoState m2 = new MonoState(); // same internal state of m1 (eg static)
以下是Robert C. Martin對此的看法: Singleton vs. Monostate (pdf)
SINGLETON 最適用於您想要通過派生來約束現有類的情況,並且您不介意每個人都必須調用 instance() 方法才能獲得訪問權限。 當您希望類的單一性質對用戶透明,或者當您希望使用單個對象的多態派生時,最好使用 Monostate。
在它的基礎上,Monostate 只是 Singleton 周圍的語法糖。 當您開始子類化時,Monostate 變得有趣,因為子類可以用不同的行為裝飾共享狀態。
一個簡單的——如果有點做作而且效率不高:)——例子:
public class GlobalTable implements Iterable<Key> {
/** Shared state -- private */
private static final Map<Key, Value> MAP = new LinkedHashMap<Key, Value>();
/** Public final accessor */
public final Value get(Key key) {
return MAP.get(key);
}
/** Public final accessor */
public final boolean put(Key key, Value value) {
return MAP.put(key);
}
/** Protected final accessor -- subclasses can use this to access
the internal shared state */
protected final Set<Key> keySet() {
return MAP.keySet();
}
/** Virtual -- subclasses can override for different behavior */
public Iterator<Key> iterator() {
return Collections.unmodifiableSet(MAP.keySet()).iterator();
}
}
現在如果我們想要索引訪問怎么辦?
public class IndexedGlobalTable extends GlobalTable {
public List<Key> getKeysAsList() {
return Collections.unmodifiableList(new ArrayList<Key>(keySet()));
}
public Key getKeyAt(int index) {
return getKeysAsList().get(index);
}
public Value getValueAt(int index) {
return get(getKeyAt(index));
}
}
排序鍵怎么樣?
public class SortedGlobalTable extends GlobalTable {
@Override
public Iterator <Key> iterator() {
return Collections
.unmodifiableSortedSet(new TreeSet<Key>(keySet())).iterator();
}
}
每當您需要數據的一個或另一個視圖時,您只需實例化適當的子類。
當然,首先全局數據是否真的是一個好主意是另一個問題,但至少 Monostate 讓您在使用它時有了更大的靈活性。
有人應該注意到單例和單態是極其危險的模式。 他們往往會被懶惰的程序員濫用,他們不想考慮他們想要制作成單例的對象的生命周期。 它們使測試變得更加困難,並創建了緊密綁定的不靈活系統。
真正需要單身或單態的情況極為罕見。 對象協作的首選方法是依賴注入。
關於這個已經寫了很多:
兩種模式之間的區別在於行為與結構之一。 SINGLETON模式強制執行奇點結構。 它可以防止創建一個以上的實例。 而MONOSTATE在不施加結構約束的情況下強制執行奇點行為。
SINGLETON 的好處
SINGLETON的成本
破壞是未定義的。 沒有很好的方法來銷毀或停用 SINGLETON。 如果您添加使實例無效的停用方法,系統中的其他模塊可能仍持有對 SINGLETON 實例的引用。 對 Instance 的后續調用將導致另一個實例被創建,從而導致兩個並發實例存在。
不繼承。 從 SINGLETON 派生的類不是單例。 如果它需要是 SINGLETON,則需要添加靜態函數和變量。
效率。 對 Instance 的每次調用都會調用 if 語句。 對於大多數這些調用,if 語句是無用的。
不透明。 SINGLETON 的用戶知道他們正在使用 SINGLETON,因為他們必須調用 Instance 方法。
MONOSTATE 的好處
透明度。 MONOSTATE 用戶的行為與常規對象的用戶沒有什么不同。 用戶不需要知道對象是 MONOSTATE。
可推導性。 單態的衍生物是單態。 事實上,一個 MONOSTATE 的所有衍生物都是同一個 MONOSTATE 的一部分。 它們都共享相同的靜態變量。
多態性。 由於 MONOSTATE 的方法不是靜態的,它們可以在派生中被覆蓋。 因此,不同的導數可以在同一組靜態變量上提供不同的行為。
定義明確的創造和毀滅。 MONOSTATE 的變量是靜態的,具有明確定義的創建和銷毀時間。
MONOSTATE 的費用
沒有轉換。 普通類不能通過派生轉換為 MONOSTATE 類。
效率。 一個 MONOSTATE 可能會經歷許多創建和破壞,因為它是一個真實的對象。 這些操作通常成本高昂。
在場。 MONOSTATE 的變量會占用空間,即使從未使用過 MONOSTATE。
敏捷軟件開發、原則、模式和實踐 Robert C. Martin
我認為大多數人對單態模式的理解是錯誤的。
這些天來,我閱讀了有關范疇論的頁面。 在范疇論中,單例集合被定義為只有一個元素的集合。 所以單例集有很多好的特性,比如你可以隨時創建一個單例集的元素,並且元素總是相同的。 事實上,這就是單態應該意味着的。 您可以隨時創建單態的實例,並且該實例始終相同。
但是,對於我們普通使用的單態來說,元素真的一樣嗎? 眾所周知,一個元素是由它的接口定義的,所以同一個元素的接口應該總是做同樣的事情並返回同樣的結果。 因此,即使 monostate 沒有非靜態成員 itelt,它們也不相同。 它根本沒有什么好的功能。
什么是真正的單態? 事實上, std::allocator
就是一個很好的例子。 它使用全局唯一內存堆,但定義了一個單態,並且std::allocator
的所有實例都具有相同的行為。 您可以隨機創建std::allocator
的實例並使用它。
更重要的是。 普通單例模式是否定義了單例集? 其實單例類本身並不是單例集,但是單例集的引用指針是單例集。)
我在這個答案中談到的單態和單態的普通使用:
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
private:
~Singleton() = default;
};
class MonoState {
public:
void setValue(int v) {
_v = v;
}
int getValue() {
return _v;
// getValue may return different result at different time.
}
private:
static inline int _v;
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.