Design patterns 单例模式的缺点是什么?
是一个完全付费的会员,但是最近它似乎被开发者世界孤立了。我仍然使用了很多单例,特别是对于,虽然您必须对多线程问题(实际上像任何类一样)小心一点,但我不明白为什么它们如此糟糕 堆栈溢出似乎特别假设每个人都同意单例是邪恶的。为什么?Design patterns 单例模式的缺点是什么?,design-patterns,singleton,Design Patterns,Singleton,是一个完全付费的会员,但是最近它似乎被开发者世界孤立了。我仍然使用了很多单例,特别是对于,虽然您必须对多线程问题(实际上像任何类一样)小心一点,但我不明白为什么它们如此糟糕 堆栈溢出似乎特别假设每个人都同意单例是邪恶的。为什么? 请用“事实、参考或特定专业知识”来支持您的答案,因为它们基本上是面向对象的全局变量,您通常可以这样设计类,这样您就不需要它们了 它很容易(ab)用作全局变量 依赖于单例的类相对来说更难单独进行单元测试 有些人还认为它是一种反模式,他们认为它被过度使用,在实际不需要类的
请用“事实、参考或特定专业知识”来支持您的答案,因为它们基本上是面向对象的全局变量,您通常可以这样设计类,这样您就不需要它们了
假设该模式用于模型的某个方面,而该方面是真正单一的,那么该模式本身并没有什么问题
我相信这种反弹是由于它的过度使用,而这反过来又是因为它是最容易理解和实现的模式。谷歌的Misko Hevery有一些关于这个主题的有趣文章 有一个单元测试示例,演示了单例如何使找出依赖链和启动或测试应用程序变得困难。这是一个相当极端的虐待例子,但他提出的观点仍然有效: 单身汉只不过是一个全球性的国家。全局状态使您的对象可以秘密地获取未在其API中声明的内容,因此,单例使您的API成为病态说谎者
指出依赖项注入使得向需要实例的构造函数获取实例变得容易,这缓解了第一篇文章中所述的糟糕的全局单例背后的潜在需求。单例使用静态方法实现。进行单元测试的人避免使用静态方法,因为它们不能被模仿或存根。这个网站上的大多数人都大力支持单元测试。最普遍接受的避免它们的惯例是使用这种模式。一些编码势利者将它们视为一种美化的全局模式。就像许多人讨厌goto声明一样,也有其他人讨厌使用全球网络的想法。我见过一些开发人员为了避免一次全球性的失败而不遗余力,因为他们认为使用一次失败是一种承认。奇怪但真实
实际上,单例模式只是一种编程技术,它是概念工具包中有用的一部分。有时,你可能会发现它是理想的解决方案,因此请使用它。但是,仅仅为了吹嘘使用设计模式而使用它,就如同拒绝使用它一样愚蠢,因为它只是一个全局模式。摘自Brian Button:
关于单例的一个相当糟糕的事情是,你不能很容易地扩展它们。如果你想改变他们的行为,你基本上必须加入一些类似的东西。另外,如果有一天你想用多种方法来完成一件事,那么根据你如何布局你的代码,改变可能是相当痛苦的 需要注意的一点是,如果您使用单例,请尝试将它们传递给需要它们的人,而不是让他们直接访问它。。。否则,如果您选择使用多种方式来完成singleton所做的事情,那么很难进行更改,因为如果每个类直接访问singleton,那么每个类都会嵌入一个依赖项 所以基本上:
公共MyConstructor(单例单例){
this.singleton=singleton;
}
而不是:
public MyConstructor(){
this.singleton=singleton.getInstance();
}
我相信这种模式被称为,通常被认为是一件好事
就像任何模式一样。。。想想看,如果它在给定的情况下使用不当或不…规则通常是被打破的,不应该毫无思想地任意应用。当您使用单例(例如,记录器或数据库连接)编写代码,然后发现您需要多个日志或多个数据库时,您会遇到麻烦 单例使得从它们移动到常规对象非常困难 而且,编写非线程安全的单例代码太容易了 您应该将所有需要的实用程序对象从一个函数传递到另一个函数,而不是使用单例。如果将所有对象包装到辅助对象中,则可以简化此操作,如下所示:
void some_class::some_function(parameters, service_provider& srv)
{
srv.get<error_logger>().log("Hi there!");
this->another_function(some_other_parameters, srv);
}
void some_class::some_函数(参数、服务提供程序和srv)
{
srv.get().log(“你好!”;
此->另一个函数(一些其他参数,srv)
public class SingletonDao {
// songleton's static variable and getInstance() method etc. omitted
public void writeXYZ(...){
synchronized(...){
// some database writing operations...
}
}
}