繁体   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