簡體   English   中英

單態與單態

[英]Monostate vs. Singleton

什么情況下會使用單態模式而不是單例來維護全局對象?

編輯:我知道單例模式和單態模式是什么。 在很多場景中也實現了 Singleton。 只想知道需要實現 MonoState 模式的場景(案例)。

例如。 我需要在我的 Windows 窗體應用程序中維護每個屏幕的列列表。 在這種情況下,我可以使用單例字典。 但是,我將 List 存儲在靜態全局變量中,並且我想提供索引器(因為如果鍵不存在,我需要動態地將新條目添加到列表中),我可以將 ScreenDetails.ScreenName 指定為鍵並獲取 ScreenDetails .ColumnsTable。 由於索引器無法對靜態類進行操作,因此我將模式更改為 Monostate。

所以我想知道哪些其他場景可能會迫使用戶使用 Monostate 而不是 Singletons。

monostate 和 singleton 是同一個獎牌的兩個面(全局狀態):

  • 單態強制一種行為(所有類實例中只有一個值)
  • 單例強制結構約束(只有一個實例)

單例使用不透明

IE:

Singleton singleton = Singleton.getInstance();

單態使用是透明的

IE:
 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,則永遠不會創建它。

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM