[英]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.