该 单身模式 是一个完全付清的成员 四人帮的 模式书,但它最近似乎是开发者世界的孤儿。我仍然使用相当多的单身人士,尤其是 工厂类虽然你必须对多线程问题(实际上是任何类)有点小心,但我不明白为什么它们太可怕了。
Stack Overflow特别假设每个人都同意Singletons是邪恶的。为什么?
请用“支持你的答案”事实,参考或具体的专业知识“
该 单身模式 是一个完全付清的成员 四人帮的 模式书,但它最近似乎是开发者世界的孤儿。我仍然使用相当多的单身人士,尤其是 工厂类虽然你必须对多线程问题(实际上是任何类)有点小心,但我不明白为什么它们太可怕了。
Stack Overflow特别假设每个人都同意Singletons是邪恶的。为什么?
请用“支持你的答案”事实,参考或具体的专业知识“
从Brian Button转述:
单身人士解决了一个(也是唯一一个)问题。
资源争用。
如果你有一些资源
(1)只能有一个实例,而且
(2)您需要管理该单个实例,
你需要一个 独生子。
没有很多例子。日志文件是最重要的。您不想只放弃单个日志文件。您想要正确刷新,同步和关闭它。这是必须管理的单个共享资源的示例。
你很少需要一个单身人士。他们不好的原因是他们感觉像是一个 全球 他们是GoF的全额支付成员 设计模式 书。
当你认为自己需要全局时,你可能会犯一个可怕的设计错误。
一些编码的势利者瞧不起他们只是一个美化的全球。就像许多人讨厌的那样 去 声明还有其他人讨厌曾经使用过的想法 全球。我见过几个开发人员为了避免a 全球 因为他们考虑使用一个作为承认失败。奇怪但真实。
在实践中 独生子 模式只是一种编程技术,是您的概念工具包的有用部分。您可能会不时发现它是理想的解决方案,因此请使用它。但是使用它只是为了让你自夸使用a 设计模式 就像拒绝使用它一样愚蠢,因为它只是一个 全球。
来自谷歌的Misko Hevery有一些关于这个话题的有趣文章......
单身人士是病态的骗子 有一个单元测试示例,说明单例如何使得很难找出依赖链并启动或测试应用程序。这是滥用的一个相当极端的例子,但他提出的观点仍然有效:
单身人士只不过是全球化的国家。全局状态使得您的对象可以秘密地掌握未在其API中声明的内容,因此,单身人士会将您的API变成病态的骗子。
所有单身人士都去了哪里 表明依赖注入使得向需要它们的构造函数获取实例变得容易,这减轻了第一篇文章中谴责的糟糕的全局Singletons背后的潜在需求。
我认为混淆是由于人们不知道Singleton模式的真实应用。我不能强调这一点。辛格尔顿是 不 包装全局变量的模式。单例模式只应用于保证 给定类的唯一一个实例 在运行时存在。
人们认为Singleton是邪恶的,因为他们将它用于全局变量。正是由于这种混乱,单身人士被人瞧不起。请不要混淆单身人士和全球人士。如果用于它的目的,您将从Singleton模式中获得极大的好处。
关于单身人士的一个相当不好的事情是你不能很容易地扩展它们。你基本上必须建立某种形式 装饰图案 或者某些这样的事情,如果你想改变他们的行为。另外,如果有一天你想要有多种方法来做这件事,那么根据你的代码布局方式进行更改可能会非常痛苦。
有一点需要注意,如果你使用单身人士,试着将它们传递给任何需要它们的人,而不是让他们直接访问它们......否则,如果你选择有多种方式来完成单身人士所做的事情,那么它将是因为每个类在直接访问单例时嵌入依赖项,所以很难改变。
所以基本上:
public MyConstructor(Singleton singleton) {
this.singleton = singleton;
}
而不是:
public MyConstructor() {
this.singleton = Singleton.getInstance();
}
我相信这种模式被称为 依赖注入 并且通常被认为是一件好事。
像任何模式一样......考虑一下并考虑它在特定情况下的使用是否不合适......规则通常会被破坏,并且 模式 如果没有想到,不应该毫无疑问地应用。
单身模式本身不是问题。问题在于,人们经常使用这种模式来开发具有面向对象工具的软件,而没有扎实地掌握OO概念。当在这种情况下引入单例时,它们往往会成长为无法管理的类,每个小用途都包含辅助方法。
从测试的角度来看,单身人士也是一个问题。他们倾向于使孤立的单元测试难以编写。 控制倒置 (IoC)和 依赖注入 是一种旨在以面向对象的方式克服这个问题的模式,它有助于单元测试。
在一个 垃圾收集 环境单例很快就会成为内存管理方面的问题。
还存在多线程场景,其中单例可能成为瓶颈以及同步问题。
使用静态方法实现单例。进行单元测试的人员可以避免使用静态方法,因为它们不能被模拟或存根。该网站上的大多数人都是单元测试的重要支持者。通常最被接受的避免它们的惯例是使用 控制反转 模式。
单身人士也很糟糕 集群。因为那时,你的应用程序中不再有“完全一个单独”。
请考虑以下情况:作为开发人员,您必须创建一个访问数据库的Web应用程序。要确保并发数据库调用不会相互冲突,请创建线程保存 SingletonDao
:
public class SingletonDao {
// songleton's static variable and getInstance() method etc. omitted
public void writeXYZ(...){
synchronized(...){
// some database writing operations...
}
}
}
因此,您确定应用程序中只存在一个单例,并且所有数据库都只通过此单例 SingletonDao
。您的生产环境现在看起来像这样:
到目前为止一切都很好。
现在,考虑您要在群集中设置Web应用程序的多个实例。现在,你突然间有这样的事情:
这听起来很奇怪,但是 现在你的申请中有很多单身人士。而这正是单身人士不应该做的事情:有很多对象。如果您(如此示例中所示)想要对数据库进行同步调用,则这尤其糟糕。
当然,这是单身人士使用不当的一个例子。但是这个例子的信息是:你不能依赖应用程序中只有一个单例实例 - 特别是在集群方面。