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

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

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


当前回答

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

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

否则,它不是单例。

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

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

其他回答

这并不是说单体本身是坏的,而是GoF设计模式是坏的。唯一真正有效的论点是,GoF设计模型不适合测试,尤其是在并行运行测试的情况下。

只要在代码中应用以下方法,使用类的单个实例就是有效的构造:

确保将用作单例的类实现了一个接口。这允许使用相同的接口实现存根或模拟确保Singleton是线程安全的。这是给的。单例应该是简单的,而不是过于复杂。在应用程序的运行时,如果需要将单例传递给给定对象,请使用构建该对象的类工厂,并让类工厂将单例实例传递给需要它的类。在测试期间,为了确保确定性行为,将单例类创建为单独的实例,作为实际类本身或实现其行为的存根/模拟,并将其原样传递给需要它的类。不要使用在测试期间创建需要单例的被测对象的类因子,因为它将传递其单个全局实例,这违背了目的。

我们已经在我们的解决方案中使用了Singleton,并取得了巨大的成功,这些成功是可测试的,确保了并行测试运行流中的确定性行为。

单例使用静态方法实现。静态方法是做单元测试的人所避免的,因为它们不能被嘲笑或拒绝。这个网站上的大多数人都是单元测试的支持者。避免这种情况的最普遍接受的惯例是使用控制模式反转。

一些代码势利者看不起他们,认为他们只是一个荣耀的全球人。就像许多人讨厌goto语句一样,也有其他人讨厌使用全局的想法。我见过几位开发人员为了避免全局性而竭尽全力,因为他们认为使用全局性是失败的承认。奇怪但真实。

在实践中,Singleton模式只是一种编程技术,它是概念工具包的有用部分。有时你可能会发现它是理想的解决方案,所以使用它。但使用它只是为了吹嘘使用设计模式,就像拒绝使用它一样愚蠢,因为它只是一个全球性的。

Singleton不是关于单个实例!

与其他答案不同,我不想谈论Singleton有什么问题,而是想告诉你,如果使用得当,它们是多么强大和可怕!

问题:在多线程环境中,Singleton可能是一个挑战解决方案:使用单线程引导过程来初始化单例的所有依赖项。问题:很难模仿单身汉。解决方案:使用工厂模式进行模拟


您可以将MyModel映射到继承它的TestMyModel类,每当注入MyModel时,您都会得到TestMyModel instread。-问题:单线程可能会导致内存泄漏,因为它们从未处理过。解决方案:好吧,把它们处理掉!在你的应用程序中实现回调以正确处理一个单件,你应该删除所有链接到它们的数据,最后:从工厂中删除它们。

正如我在标题中所说的,singleton不是关于单个实例的。

singleton提高了可读性:您可以查看您的类,看看它注入了什么样的singleton,以确定它的依赖项是什么。singleton改进了维护:一旦你从一个类中删除了一个依赖项,你就删除了一些单例注入,你就不需要去编辑其他类的一个大链接,这些类只是移动了你的依赖项(这是我的臭代码@Jim Burger)Singleton提高了内存和性能:当应用程序中发生了一些事情,并且需要很长的回调链才能传递时,您正在浪费内存和性能,通过使用Singleton,您正在削减中间人,并提高性能和内存使用率(通过避免不必要的局部变量分配)。

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