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

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

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


当前回答

在我的头顶上:

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

其他回答

摘自Brian Button:

它们通常被用作全局实例,为什么如此糟糕?因为您将应用程序的依赖项隐藏在代码中,而不是通过接口公开它们。让一些东西全球化以避免传播是一种代码气味。他们违反了单一责任原则:因为他们控制自己的创作和生命周期。它们固有地导致代码紧密耦合。这使得在许多情况下,在测试中伪造它们相当困难。它们在应用程序的整个生命周期中携带状态。另一个对测试的打击,因为您可能会遇到一种需要订购测试的情况,这对于单元测试来说是一个很大的禁忌。为什么?因为每个单元测试都应该彼此独立。

这是我认为目前为止答案中缺少的:

如果每个进程地址空间需要一个此对象的实例(并且您尽可能确信此需求不会改变),那么应该将其设置为单例。

否则,它不是单例。

这是一个非常奇怪的要求,用户几乎不感兴趣。进程和地址空间隔离是一个实现细节。它们仅在用户希望使用kill或Task Manager停止应用程序时才会对用户产生影响。

除了构建一个缓存系统之外,没有太多的原因可以让您如此确信每个进程只应有一个实例。日志记录系统怎么样?对于每线程或更细粒度的消息可能更好,这样您可以更自动地跟踪消息的来源。应用程序的主窗口如何?这取决于;也许出于某种原因,您希望所有用户的文档都由同一进程管理,在这种情况下,该进程中会有多个“主窗口”。

单身者在集群方面也很糟糕。因为这样,您的应用程序中就不再有“恰好一个单例”了。

考虑以下情况:作为开发人员,您必须创建一个访问数据库的web应用程序。为了确保并发数据库调用不会相互冲突,您可以创建一个线程保存SingletonDao:

public class SingletonDao {
    // songleton's static variable and getInstance() method etc. omitted
    public void writeXYZ(...){
        synchronized(...){
            // some database writing operations...
        }
    }
}

因此,您可以确定应用程序中只存在一个singleton,并且所有数据库都通过这一个也是唯一的SingletonDao。您的生产环境现在如下所示:

到目前为止一切都很好。

现在,考虑您想在集群中设置web应用程序的多个实例。现在,你突然有了这样的想法:

这听起来很奇怪,但现在您的应用程序中有很多单例。这正是单例不应该是的:它有很多对象。如果您想对数据库进行同步调用,如本例所示,这尤其糟糕。

当然,这是单例用法错误的一个例子。但是这个例子的信息是:你不能相信你的应用程序中只有一个单例实例,特别是在集群方面。

Singleton是一种模式,可以像任何其他工具一样被使用或滥用。

单例的坏部分通常是用户(或者我应该说单例不适合用于它不适合做的事情)。最大的违规行为是使用单例作为假全局变量。

单身汉——反模式!马克·拉德福德(Mark Radford)的《过载杂志》(Overload Journal#57–2003年10月)很好地解释了为什么辛格尔顿被视为反模式。本文还讨论了替代Singleton的两种替代设计方法。