单例模式是GoF模式书中一个完全付费的成员,但最近它似乎被开发人员世界孤立了。我仍然使用了相当多的单线程,特别是对于工厂类,虽然你必须对多线程问题(像任何类一样)小心一点,但我不明白为什么它们如此糟糕。

Stack Overflow似乎特别假设每个人都认为Singleton是邪恶的。为什么?

请用“事实、参考资料或具体专业知识”来支持您的回答


当前回答

当几个人(或团队)达成类似或相同的解决方案时,就会出现一种模式。许多人仍然使用原始形式的单件或使用工厂模板(Alexandrescu的《现代C++设计》中有很好的讨论)。并发性和管理对象生存期的困难是主要障碍,前者很容易按照您的建议进行管理。

和所有的选择一样,辛格尔顿也有其沉浮的部分。我认为它们可以适度使用,特别是对于在应用程序寿命期内仍然存在的对象。事实上,它们类似于(而且很可能是)全球性的,这可能引发了纯粹主义者。

其他回答

在我的头顶上:

它们加强了紧密耦合。如果您的单例驻留在与其用户不同的程序集上,那么如果没有包含单例的程序集,using程序集就永远无法运行。它们允许循环依赖,例如,程序集A可以有一个依赖于程序集B的单例,而程序集B可以使用程序集A的单例。所有这些都不会破坏编译器。

当您使用单例(例如,记录器或数据库连接)编写代码时,然后发现您需要多个日志或多个数据库,您就遇到了麻烦。

单身者很难从他们身上转移到普通物体上。

此外,编写非线程安全的单例也太容易了。

您应该将所有需要的实用程序对象从一个函数传递到另一个函数,而不是使用单例。如果您将所有这些对象包装到一个辅助对象中,这可以简化,如下所示:

void some_class::some_function(parameters, service_provider& srv)
{
    srv.get<error_logger>().log("Hi there!");
    this->another_function(some_other_parameters, srv);
}

来自谷歌的Misko Hevery就这个话题发表了一些有趣的文章。。。

单身者是病态的骗子。有一个单元测试示例,说明了单身者如何难以找出依赖链并启动或测试应用程序。这是一个相当极端的虐待例子,但他提出的观点仍然有效:

单身者无非是全球国家。全局状态使您的对象可以秘密地获取未在其API中声明的内容,因此,Singleton会将您的API变成病态的骗子。

所有的Singleton都去哪儿了,这表明依赖注入使得向需要实例的构造函数提供实例变得容易,这减轻了第一篇文章中谴责的糟糕的全局Singleton背后的潜在需求。

我不打算评论善/恶的论点,但自从春天来临以来,我就没有使用过它们。使用依赖注入几乎消除了我对单例、服务定位器和工厂的需求。我发现这是一个更加高效和干净的环境,至少对于我所做的工作类型(基于Java的web应用程序)来说是如此。

假设该模式用于模型的某个方面,而该方面是真正的单一模式,则该模式没有本质上的错误。

我认为这种反弹是由于它的过度使用,而这反过来又是因为它是最容易理解和实施的模式。