为什么在Java代码中实现单例模式(有时)被认为是Java世界中的反模式?

我看到一些人在评论Singleton Pattern是反模式。 我想知道为什么?

testing

一个原因是单身testing不容易处理。 你不能控制实例化,并且根据它们的性质可能会在调用之间保持状态。

出于这个原因, dependency injection的原理是stream行的。 每个类都被注入(configuration)它们需要运行的类(而不是通过单例访问器派生),因此testing可以控制使用哪个相关类实例(并在需要时提供模拟)。

像Spring这样的框架将控制其对象的生命周期,并经常创build单例,但是这些对象被框架注入到它们的依赖对象中。 因此,代码库本身不会将对象视为单例。

例如,而不是这个(例如)

public class Portfolio { private Calculator calc = Calculator.getCalculator(); } 

你会注入计算器:

 public class Portfolio { public Portfolio(Calculator c) { this.calc = c; } } 

因此, Portfolio对象不知道/关心Calculator有多less个实例存在。 testing可以注入一个虚拟Calculator ,使testing变得简单。

并发

通过将自己限制为一个对象的一个​​实例,线程的选项是有限的。 访问单例对象可能不得不被保护(例如通过同步)。 如果您可以维护这些对象的多个实例,那么您可以将实例的数量定制到您正在运行的线程,并提高代码库的并发能力。

我个人的意见是违反单一责任原则。 单身人士对象既负责他们的目的,也负责控制我们认为是错误的实例数量。

这就是为什么很多人将控制委托给工厂对象。

[可变]单例是反模式的反模式。

重要的底层反模式是全局状态(或环境状态)。 在全球化的状态下,你的程序中有一个很大的依赖关系博客。 这确实会影响testing,但这只是不良编程影响的一部分。

在此基础上,Singleton增加了完全不必要的复杂度,而不是简单地声明可变static字段。

单身人士本身并不一定是反模式,但是他们只有很less的好处,并且在使用错误(经常发生)时变成反模式。

通常,单身人士根本不是单身人士,而是“变相的全球变数”。 而且,当“只有一个实例”属性实际上不是优点时,通常使用它们。 (这又被多次实施错误的事实所平衡)。

除此之外,如果想要控制实例化,那么在实现multithreading(通常做错或效率低下)时,实现起来会非常棘手,并且会失去大部分好处。

如果你想控制实例化,你需要在程序早期的某个时候手动完成,但是你也可以创build一个普通对象的实例并传递给它。
如果销毁顺序有任何问题,您也需要手动执行此操作。 主函数中的一个自动对象更加简洁和简单。

你可能对单例有很多要求:

  1. 懒惰的初始化;
  2. 妥善处理;
  3. 范围界定(例如每个线程一个)。

通常情况下,您的应用程序中也会包含大量的单例, Singleton模式不允许使用可重用的代码。 所以,如果你想为所有的单身人士实现所有这些担心,你会立即看到它的反模式质量。

奇怪。 看来,单身人士的错误实施是一种“反模式”,而不是单身人士本身。

我想我们已经忘记了每个程序都必须从某个地方开始。 每一个抽象都必须有一个具体的实现,最终每一个依赖将最终得到解决,否则你的应用程序将不会有太多的用处。

大多数DI框架允许实例化一个类作为一个单例,它只是为你处理。 如果你select自己做DI,注入一个singleton不是问题。 一个Singleton IS也可以testing,如果你使用DI来注入它,不会使一个类不稳定。

国际海事组织,像其他所有模式(包括DI和IoC),这是一个工具。 有时适合,有时候不适合。