单例模式是GoF模式书中一个完全付费的成员,但最近它似乎被开发人员世界孤立了。我仍然使用了相当多的单线程,特别是对于工厂类,虽然你必须对多线程问题(像任何类一样)小心一点,但我不明白为什么它们如此糟糕。
Stack Overflow似乎特别假设每个人都认为Singleton是邪恶的。为什么?
请用“事实、参考资料或具体专业知识”来支持您的回答
单例模式是GoF模式书中一个完全付费的成员,但最近它似乎被开发人员世界孤立了。我仍然使用了相当多的单线程,特别是对于工厂类,虽然你必须对多线程问题(像任何类一样)小心一点,但我不明白为什么它们如此糟糕。
Stack Overflow似乎特别假设每个人都认为Singleton是邪恶的。为什么?
请用“事实、参考资料或具体专业知识”来支持您的回答
当前回答
太多人将非线程安全的对象放在单例模式中。尽管DataContext不是线程安全的,纯粹是一个工作单元对象,但我已经看到了DataContext(LINQ to SQL)以单例模式完成的示例。
其他回答
从纯粹主义的观点来看,单身汉是不好的。
从实践的角度来看,单例是开发时间与复杂性的权衡。
如果你知道你的应用程序不会有太大的变化,那么它们很好用。只要知道,如果您的需求以意外的方式发生变化(在大多数情况下这是很正常的),您可能需要重构。
单体有时也会使单元测试复杂化。
单线图的一个相当糟糕的地方是,你不能很容易地扩展它们。如果你想改变他们的行为,你基本上必须构建某种装饰模式或类似的东西。此外,如果有一天你想用多种方式来做这一件事,那么改变可能会很痛苦,这取决于你如何布局代码。
需要注意的一点是,如果您确实使用了单体,请尝试将它们传递给需要它们的人,而不是让他们直接访问。。。否则,如果您选择使用多种方式来完成单例所做的事情,那么如果每个类都直接访问单例,那么很难进行更改,因为每个类都嵌入了一个依赖项。
所以基本上:
public MyConstructor(Singleton singleton) {
this.singleton = singleton;
}
而不是:
public MyConstructor() {
this.singleton = Singleton.getInstance();
}
我认为这种模式被称为依赖注入,通常被认为是一件好事。
就像任何模式一样。。。思考它,并考虑它在给定情况下的使用是否不合适。。。规则通常是被打破的,模式不应该随意应用。
如果你使用得当且最少,单身汉并不是邪恶的。还有很多其他好的设计模式可以在某一点上替代单例的需求(也可以提供最佳结果)。但是一些程序员不知道这些好模式,在所有情况下都使用单例模式,这使得单例模式对他们来说是有害的。
我想谈谈公认答案中的4点,希望有人能解释我为什么错了。
为什么在代码中隐藏依赖项不好?已经有几十个隐藏的依赖项(C运行时调用、OS API调用、全局函数调用),单例依赖项很容易找到(搜索instance())。“使某个东西全局化以避免传递它是一种代码气味。”为什么不传递某个东西以避免使其成为单例代码气味?如果您通过调用堆栈中的10个函数传递一个对象,只是为了避免一个单例,那么这样做好吗?单一责任原则:我认为这有点模糊,取决于你对责任的定义。一个相关的问题是,为什么将这个特定的“责任”添加到一个班级中很重要?为什么将一个对象传递给一个类比将该对象作为类内的单例使用更紧密地耦合?为什么会改变国家的持续时间?单例对象可以手动创建或销毁,因此控件仍然存在,您可以使其生存期与非单例对象的生存期相同。
关于单元测试:
并非所有的类都需要是单位已测试并非所有需要成为单元的类测试需要更改单例的实现如果它们确实需要进行单元测试确实需要改变实施方式,很容易从使用singleton来实现通过依赖项传递给它的singleton注射
来自谷歌的Misko Hevery就这个话题发表了一些有趣的文章。。。
单身者是病态的骗子。有一个单元测试示例,说明了单身者如何难以找出依赖链并启动或测试应用程序。这是一个相当极端的虐待例子,但他提出的观点仍然有效:
单身者无非是全球国家。全局状态使您的对象可以秘密地获取未在其API中声明的内容,因此,Singleton会将您的API变成病态的骗子。
所有的Singleton都去哪儿了,这表明依赖注入使得向需要实例的构造函数提供实例变得容易,这减轻了第一篇文章中谴责的糟糕的全局Singleton背后的潜在需求。