【发布时间】:2012-07-02 10:45:34
【问题描述】:
我在 SO 中看到有人评论说单例模式是一种反模式。我想知道为什么?
【问题讨论】:
标签: java design-patterns singleton
我在 SO 中看到有人评论说单例模式是一种反模式。我想知道为什么?
【问题讨论】:
标签: java design-patterns singleton
测试
一个原因是单例不容易处理单元测试。您无法控制实例化,并且就其本质而言,它们可能会在调用之间保留状态。
因此,dependency injection 的原则很受欢迎。每个类都被注入(配置)了它们需要运行的类(而不是通过单例访问器派生),因此测试可以控制要使用的依赖类实例(并在需要时提供模拟)。
诸如Spring之类的框架会控制其对象的生命周期,并且经常会创建单例,但是这些对象是由框架注入到它们的依赖对象中的。因此,代码库本身不会将对象视为单例。
例如而不是这个(例如)
public class Portfolio {
private Calculator calc = Calculator.getCalculator();
}
你会注入计算器:
public class Portfolio {
public Portfolio(Calculator c) {
this.calc = c;
}
}
因此Portfolio 对象不知道/关心Calculator 的实例有多少。测试可以注入一个虚拟的Calculator,使测试变得容易。
并发
通过将自己限制在一个对象的一个实例上,线程的选项是有限的。可能必须保护对单例对象的访问(例如,通过同步)。如果您可以维护这些对象的多个实例,那么您可以根据正在运行的线程调整实例数量,并增加代码库的并发能力。
【讨论】:
我个人的看法是,它违反了单一责任原则。单例对象负责它们的目的和控制它们产生的实例数量,我认为这是错误的。
这就是为什么很多人将控制权委托给工厂对象的原因。
【讨论】:
[Mutable] Singleton 是一种反模式的反模式。
重要的底层反模式是全局状态(或环境状态)。使用全局状态,您可以在整个程序中拥有一个很大的依赖关系博客。这确实会影响测试,但这只是不良编程后果的一部分。
在此基础上,与简单地声明可变的 static 字段相比,Singleton 增加了完全不必要的复杂程度。
【讨论】:
这样的单例不一定是反模式,但它们只有很少的好处,并且在使用错误时会成为反模式(这种情况经常发生)。
通常,单例根本不是单例,而是“伪装的全局变量”。此外,当“只有一个实例”属性实际上不是优势时,通常会使用它们。 (这再次被许多次实现同时错误的事实所平衡)。
除此之外,考虑到多线程实现它们可能会很棘手(通常做错或效率低下),如果你想控制它们的实例化,它们就会失去大部分好处。
如果您想控制实例化,您需要在程序早期的某个时间点手动完成,但您也可以只创建一个普通对象的实例并继续传递。
如果破坏顺序有任何问题,您也需要手动实现它。主函数中的单个自动对象更加简洁和容易。
【讨论】:
你可能对单例有很多要求:
通常,您的应用中也会有大量的单例,而 单例 模式不允许可重用代码。因此,如果您想为所有单例实现所有这些问题,您将立即看到它的反模式质量。
【讨论】:
奇怪。似乎单例的错误实现是一种“反模式”,而不是单例本身。
我想我们忘记了每个程序都必须从某个地方开始。每个抽象都必须有一个具体的实现,最终每个依赖项最终都会得到解决,否则您的应用程序将没有多大用处。
大多数 DI 框架允许将类实例化为单例,它只是为您处理。如果您选择自己进行 DI,注入单例不是问题。 Singleton 也是可测试的,如果您使用 DI 注入它,不会使类变得不稳定。
IMO,就像其他所有模式(包括 DI 和 IoC)一样,它是一种工具。有时合适,有时不合适。
【讨论】: