为啥 Singleton 被认为是反模式? [复制]

Posted

技术标签:

【中文标题】为啥 Singleton 被认为是反模式? [复制]【英文标题】:Why is Singleton considered an anti-pattern? [duplicate]为什么 Singleton 被认为是反模式? [复制] 【发布时间】:2012-09-27 03:47:37 【问题描述】:

可能重复:What is so bad about Singletons?

Singleton Design Pattern: Pitfalls

Singleton anti-pattern

我最近听说 Singleton 是一种反模式。我知道这与创建一个类单例就像使该唯一实例成为全局变量这一事实有关,但它的作用远不止于此(限制该对象的实例数量,管理实例化等)。

为什么 Singleton 被认为是一种反模式?还有什么替代方案?

【问题讨论】:

我知道 Singleton 被过度使用了——我见过人们在不应该使用它的情况下使用它——但这是我第一次听说它被称为反模式。从那时起,我一直在谷歌上搜索,发现很多人认为它是一种反模式,但我没有找到任何可接受的解释...... Dour High Arch:只需谷歌“单例反模式”和你会发现数百篇关于它的文章......但我找不到任何真正证明它的文章。这就是我在这里问的原因。 杜尔高拱门:没有重复。我确实找到了那个帖子。但它没有回答:“为什么 Singleton 被认为是反模式?还有哪些替代方案?” PS:GG删除你之前的评论。 不错的链接:ibm.com/developerworks/webservices/library/co-single/index.html 如果您查看 Dour 的链接,在 cmets 中还有另一个指向 blog post 的链接,它确实很好地回答了问题的第一部分。此外,至于替代方案,另一篇 SO 帖子讨论了 DI(依赖注入)。如果这不能回答您的两个问题,您能否详细说明您在寻找什么? 在这里查看我的答案:michaelsafyan.com/tech/design/patterns/singleton 【参考方案1】:

为了帮助回答,这里是关于反模式评论的更多信息:

它被过度使用,在某些情况下引入了不必要的限制 实际上不需要类的唯一实例,并且 将全局状态引入应用程序

发件人:http://en.wikipedia.org/wiki/Singleton_pattern

有关更多信息,您可以查看:https://www.michaelsafyan.com/tech/design/patterns/singleton

这里是上面博客的一个很好的结局:

简而言之,单例模式使代码更复杂,更没用, 重用或测试真的很痛苦。消除单例可以是 很棘手,但值得一试。

好的,所以,本段很好地描述了它是反模式的原因,并且正如作者所说,它将您的代码与单例紧密耦合。

如果您发现要使用单例,您可能需要考虑您的设计,但有时它很有用。

例如,曾经我必须编写一个最多可以有一个数据库连接的应用程序来处理数千个请求。所以,单例是有意义的,因为我的资源受限于只有一个实例。

但是,这一般是用来简化代码的,不考虑会引入的困难。

例如,这也适用于静态类,如果您进行单元测试或具有并发性,那么一个请求的状态将改变状态,这可能会导致问题,因为调用实例的类可能会假设该状态和预期的一样。

我认为挑战使用的最好方法是考虑如果您的程序是多线程的,如何处理它,而一种简单的方法是对它进行单元测试,如果您有多个测试同时运行时间。

如果你发现还需要它,那就用它,但要意识到以后会遇到的问题。

【讨论】:

好答案。只是想补充一点,即使您的数据库示例也应该在没有单例的情况下解决。最简单的解决方案(显然不知道确切的用例)是拥有一个返回单个实例的工厂。这是一种更优雅的方法,并且不违反 SRP(就像单例模式一样)。此外,如果情况发生变化并且您的应用程序需要修改以允许多个数据库连接,那么与单例相比,重构将是一件轻而易举的事。从字面上看,没有任何情况下单例比另一种模式更可取。 @Creynders - 实际上我的问题是通过运行一个单独的程序来解决的,它只是在队列中查找并处理它找到的内容,所以它只有一个连接,但是,一个单例可能已经很适合那里。我遇到过类似的问题,我可能只允许 5 个连接,并且类似于 Singleton 的东西可能会起作用,因为它基本上是一个功能类似于 Singleton 的工厂。 上面 Michael Safyan 的文章是我为单身人士找到的最好的一篇 数据库是反模式的一个很好的例子——围绕当前的性能假设构建整个程序逻辑。 限制使用一个数据库并不是使用单例的好理由。这就像需要输出一行文本一样,因此您添加代码以确保仅调用一次 print。但是有一个很好的用途:您有一个损坏的类,而不是对单个资源的硬编码假设(而不是通过构造函数排除数据库并使用工厂)。该课程需要时间来修复,现在您只需要一个实例。因此将其设为单例,不是表示我们只想要一个实例,而是表示它仅限于一个实例。【参考方案2】:

我不会完全认为单例是一种反模式。

然而,单例基本上是一种使用全局变量的方式。全局变量很糟糕,因为系统中任何地方的任何代码都可以更改它们的值。因此,在调试时,要找出导致 Singleton 的当前状态的代码路径可能具有挑战性。

【讨论】:

错误。全局变量如果隐藏则无法更改。 我从未听说过 C++ 标准中的“隐藏”,您能详细说明一下吗? @Joky 我假设 hidden = abstracted。 不是这个原因。 Singleton 做了 3 件事(违反了单一职责原则):它做它的主要职责,它是一个工厂(参见工厂模式),它为单个实例提供保险(这是一个坏主意,除非是临时措施,标记一个类需要重复,这样可以有多个实例)。 没有sate的单身人士怎么办,当然所有单身人士都应该如此。【参考方案3】:

单例通常执行不力。请参阅双重检查锁定。

Singleton 实例在哪个范围内必须是唯一的。比如多线程环境、集群等。

单例可能很难测试。

分配给 Singleton 的内存无法释放。

由于过度使用单例,您会从面向对象编程转向过程编程。

【讨论】:

"分配给单例的内存无法释放。"也许不需要释放内存,这就是为什么将它放在 Singleton 中?另外,它可以在需要时自行释放内存。【参考方案4】:

我认为它被认为是一种反模式,因为单例类不能被其他对象以正常方式实例化(除非通过调用这种通常称为“getInstance”的方法)。因此,看起来该类被直接使用,而不是先实例化它来创建可用对象。

我同意你的观点,Singleton 可以作为全局唯一实例。我从一些人那里了解到,作为 Singleton 的替代方案,我们可以使用静态和/或最终变量,我们还可以使用枚举(这样我们就可以将多个变量分组到一个组名下,就像我们在使用普通类/对象)。

但是,这些替代方案只能与 Singleton 类在存储状态/值方面的能力相匹配。如果我们需要使用独特的函数,那些静态/最终变量和枚举将无济于事。在我看来,这就是我们需要使用单例类的情况(当我们需要一些独特的函数来处理静态/最终状态/值时)。

干杯... :))

【讨论】:

以上是关于为啥 Singleton 被认为是反模式? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

关于“贫血领域模型”被认为是反模式[闭合]的具体例子

在另一个被认为是反模式的承诺中使用承诺?

为啥不鼓励使用单例模式? [复制]

如何在TypeScript中定义Singleton

为啥 Borg 模式比 Python 中的 Singleton 模式更好

为啥 Borg 模式比 Python 中的 Singleton 模式更好