单例模式是GoF模式书中一个完全付费的成员,但最近它似乎被开发人员世界孤立了。我仍然使用了相当多的单线程,特别是对于工厂类,虽然你必须对多线程问题(像任何类一样)小心一点,但我不明白为什么它们如此糟糕。
Stack Overflow似乎特别假设每个人都认为Singleton是邪恶的。为什么?
请用“事实、参考资料或具体专业知识”来支持您的回答
单例模式是GoF模式书中一个完全付费的成员,但最近它似乎被开发人员世界孤立了。我仍然使用了相当多的单线程,特别是对于工厂类,虽然你必须对多线程问题(像任何类一样)小心一点,但我不明白为什么它们如此糟糕。
Stack Overflow似乎特别假设每个人都认为Singleton是邪恶的。为什么?
请用“事实、参考资料或具体专业知识”来支持您的回答
当前回答
来自谷歌的Misko Hevery就这个话题发表了一些有趣的文章。。。
单身者是病态的骗子。有一个单元测试示例,说明了单身者如何难以找出依赖链并启动或测试应用程序。这是一个相当极端的虐待例子,但他提出的观点仍然有效:
单身者无非是全球国家。全局状态使您的对象可以秘密地获取未在其API中声明的内容,因此,Singleton会将您的API变成病态的骗子。
所有的Singleton都去哪儿了,这表明依赖注入使得向需要实例的构造函数提供实例变得容易,这减轻了第一篇文章中谴责的糟糕的全局Singleton背后的潜在需求。
其他回答
单身汉还不错。只有当你做了一些全球独一无二的东西而不是全球独一无二的时候,才是糟糕的。
然而,有“应用程序范围服务”(想想让组件交互的消息传递系统)-这是一个单例调用,一个“MessageQueue”-类,它有一个方法“SendMessage(…)”。
然后,您可以在各地执行以下操作:
MessageQueue.Current.SendMessage(新邮件到达消息(…));
当然,还要做到:
MessageQueue.Current.RegisterReceiver(this);
在实现IMessageReceiver的类中。
Singleton是一种模式,可以像任何其他工具一样被使用或滥用。
单例的坏部分通常是用户(或者我应该说单例不适合用于它不适合做的事情)。最大的违规行为是使用单例作为假全局变量。
这并不是说单体本身是坏的,而是GoF设计模式是坏的。唯一真正有效的论点是,GoF设计模型不适合测试,尤其是在并行运行测试的情况下。
只要在代码中应用以下方法,使用类的单个实例就是有效的构造:
确保将用作单例的类实现了一个接口。这允许使用相同的接口实现存根或模拟确保Singleton是线程安全的。这是给的。单例应该是简单的,而不是过于复杂。在应用程序的运行时,如果需要将单例传递给给定对象,请使用构建该对象的类工厂,并让类工厂将单例实例传递给需要它的类。在测试期间,为了确保确定性行为,将单例类创建为单独的实例,作为实际类本身或实现其行为的存根/模拟,并将其原样传递给需要它的类。不要使用在测试期间创建需要单例的被测对象的类因子,因为它将传递其单个全局实例,这违背了目的。
我们已经在我们的解决方案中使用了Singleton,并取得了巨大的成功,这些成功是可测试的,确保了并行测试运行流中的确定性行为。
单例使用静态方法实现。静态方法是做单元测试的人所避免的,因为它们不能被嘲笑或拒绝。这个网站上的大多数人都是单元测试的支持者。避免这种情况的最普遍接受的惯例是使用控制模式反转。
我对单身汉如何糟糕的回答总是,“他们很难做对”。语言的许多基本组件都是单体(类、函数、名称空间甚至运算符),计算的其他方面(本地主机、默认路由、虚拟文件系统等)也是如此,这并非偶然。虽然它们会时不时地带来麻烦和挫折,但也能让很多事情变得更好。
我看到的两个最大的错误是:将其视为一个全球性的问题&未能定义Singleton闭包。
每个人都把辛格尔顿说成是全球性的,因为他们基本上是全球性的。然而,在一个全球性的世界中,很多(不幸的是,并非所有)糟糕的地方并不是来自于全球化,而是来自于你如何使用它。辛格尔顿也是如此。事实上,“单个实例”并不意味着“全局可访问”。它更多的是一种自然的副产品,而且考虑到我们所知道的所有负面影响,我们不应该急于利用全球可访问性。一旦程序员看到Singleton,他们似乎总是通过它的实例方法直接访问它。相反,您应该像导航任何其他对象一样导航到它。大多数代码甚至不应该意识到它正在处理Singleton(松耦合,对吗?)。如果只有一小部分代码像访问全局对象一样访问该对象,那么很多危害都会消除。我建议通过限制对实例函数的访问来实施它。
辛格尔顿的背景也非常重要。Singleton的定义特征是“只有一个”,但事实是它在某种上下文/名称空间中是“唯一的”。它们通常是以下之一:每个线程、进程、IP地址或集群一个,但也可以是每个处理器、机器、语言名称空间/类加载器/任何东西、子网、Internet等。
另一个不太常见的错误是忽视单身汉的生活方式。仅仅因为只有一个,并不意味着Singleton是某种无所不能的“一直都是,永远都会是”,也不是普遍可取的(没有开始和结束的对象违反了代码中所有有用的假设,只应在最绝望的情况下使用。
如果你避免了这些错误,辛格尔顿仍然可以成为一名医院,但它已经做好了准备,可以看到许多最严重的问题得到了显著的缓解。想象一个Java Singleton,它被明确定义为每个类加载器一次(这意味着它需要线程安全策略),具有定义的创建和销毁方法,以及指定何时和如何调用它们的生命周期,其“实例”方法具有包保护,因此通常可以通过其他非全局对象访问。仍然是潜在的麻烦来源,但麻烦肯定要少得多。
可悲的是,他们没有教好如何做单身汉的例子。我们教坏的例子,让程序员暂时不用它们,然后告诉他们它们是坏的设计模式。