design-patterns - 单例模式的缺点是什么?

  显示原文与译文双语对照的内容

单例模式是一个最多全部支付的成员四巨头 &似乎相当孤立的模式书,但它很晚了开发商的世界。 我仍然使用大量的单身人士,尤其是对工厂类和向我们的客户都必须认真考虑多线程问题( 像任何类实际上),逾期不明白为什么它们如此不糟糕了一点。

堆栈溢出似乎假设每个人都认为单例是邪恶的。 为什么?

时间:

Brian按钮中解释:

  1. 它们通常用作全局实例,为什么如此糟糕? 因为在代码中隐藏应用程序的依赖项,而不是通过接口公开它们。 使全局避免传递它是一个 代码异味

  2. 它们违反了 单一职责原则(Single Responsibility Principle): 因为他们控制自己的创建和生命周期。

  3. 它们本质上导致代码被紧密的耦合。 这使得在许多情况下,在测试中伪装它们是困难的。

  4. 它们在应用程序的生命周期中携带状态。 另一个测试是为了测试,因为你可以在测试需要订购的情况下进行测试,这对于单元测试来说是一个大的否。 为什么因为每个 单元测试 都应该独立于另一个。

单例解决一个( 只有一个) 问题。

资源争用。

如果你有一些资源

( 1 ) 只能有一个实例,

( 2 ) 你需要管理该单个实例,

你需要一个单张

没有很多示例。日志文件是大的。 你不想只丢弃一个日志文件。 要刷新,同步并正确关闭它。 这是一个必须管理的单一共享资源的示例。

很少需要一个单独的。 它们之所以是坏的是,他们觉得自己像个全局的和各自的全部支付最成员四巨头 &设计模式 书。

当你认为你需要一个全局的时候,你可能在犯一个可怕的设计错误。

Misko Hevery,来自谷歌,有一些关于这个主题的有趣文章。。

原先单件是病理撒谎者有一个单元测试示例,演示了单例模式会使我们很难找出依赖链和启动或者测试一个应用程序。 这是滥用的一个相当极端的例子,但他的观点仍然有效:

单例只是全局状态。 全局状态使你的对象能够秘密地获取那些不能在它的api中声明的东西,并且作为一个结果,单例将你的api变成病态的骗子。

在第一个 article, ,其中有所有的单身人士消失还提高了点,依赖注入,制定满足可以轻而易举的实例的构造函数需要它们,从而大大减少底层需要背后的不好的全局单例模式

单单例的一个缺点是你不能很容易地扩展它们。 你基本上需要用某种装饰模式来构建,如果你想改变它们的行为,你也需要这样做。 你如何将这些code,还要布局上如果有一天你想有多个的途径来实现,首先,它可以是相当痛苦的来更改,depending.

有一件事需要注意,如果你使用单例模式,试着将它们传递给需要它们的人,而不是让他们直接访问它们- - 。 否则如果你选择让过多的途径来实现该单一实例的用途,它将是相当难以改变的记忆因为每个类直接嵌入一个依赖项,如果它将访问该单例。

所以基本上:


public MyConstructor(Singleton singleton) {
 this.singleton = singleton;
}

而不是:


public MyConstructor() {
 this.singleton = Singleton.getInstance();
}

我认为这种模式叫做依赖注入,通常认为是一个好东西。

和任何模式一样,考虑一下它并考虑它在给定情况下是否不合适。 规则经常被破坏,而模式不应该被应用到 willy 。

singleton模式本身不是一个问题。 问题在于,人们所使用的模式经常与object-oriented开发软件的工具,但有一个坚实地掌握面向对象的基本概念。 于每一个小不相关use,当介绍了单例模式在这里上下文中的他们往往能生成人体难以管理类包含 helper methods.

单例也是一个测试视角中的问题。 他们倾向于使独立的unit-tests难以编写。 控制反转 ( IoC ) 和 依赖注入 模式都打算在一个object-oriented方式使得它自己来克服这里问题的单元测试。

垃圾收集中, 环境单例可以很快成为内存管理的问题。

另外还有multi-threaded场景,单身人士可以成为瓶颈以及在了同步问题。

当谈到聚类单身人士也很糟糕。 因为这样的话,你的应用程序里就没有"恰好一个单一单曲"了。

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


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

因此,你确信应用程序中只有一个单一实例,并且所有数据库都通过这个,并且只有 SingletonDao 。 你的生产环境现在看起来像这样: Single Singleton

目前为止一切正常。

现在,考虑在集群中设置多个web应用程序实例。 现在,你突然拥有了类似这样的东西:

Many singletons

在你的应用程序中,这听起来有些怪异,但 singletons.现在你有很多 而这正是一个单一的事实: 有许多对象的。 这尤其糟糕,如下面的示例所示,希望对数据库进行同步调用。

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

...