【发布时间】:2009-09-19 11:43:08
【问题描述】:
不确定使用严格的全局状态实现是否会失败。单例何时不适合应用程序?
【问题讨论】:
-
Here 是关于单例作为反模式的好读物。
标签: java design-patterns singleton
不确定使用严格的全局状态实现是否会失败。单例何时不适合应用程序?
【问题讨论】:
标签: java design-patterns singleton
如果您正在进行单元测试,单例通常是个坏主意,不进行单元测试(或 BDD 或验收测试)通常是个坏主意。
使对象具有全局状态意味着您编写的涉及这些对象的单元测试将彼此隔离和脱节。相反,您将不得不担心为每个测试重置状态,相信我……这永远不会 100% 完成。如果您不重置全局状态,那么您会开始在测试中遇到非常奇怪且难以调试的错误,这些错误会浪费时间。
全局状态还会增加代码的耦合度,使其难以重构。
理想的方法是使用 IoC/DI 容器(Spring、Guice 等)来请求对象。这些容器通常具有使对象显示为“单例”的方法,但它们也有根据情况修改该行为的方法(即单元测试与您的域代码)。
这当然取决于您问题的大小。如果你正在拼凑一个 4-class 测试台来尝试一些东西,那么继续使用 Singleton。但是,一旦该项目开始运作并变得更大、更复杂,那么就将 Singleton 重构出来。
【讨论】:
Google Tech Talks 前段时间有一个关于Global State and Singletons 的精彩演示。静态单例模式是邪恶的,因为它会导致不必要的副作用并使代码无法测试。静态单例是全局变量的面向对象版本。
解决方案是只创建一个对象的实例,然后通过依赖注入将其传递给它的用户。 DI 框架,例如Guice,使定义良好的单例变得容易(在 Guice 中,只需使用 @Singleton 注释一个类)。有一个类似的 Tech Talk,称为 Don't Look For Things!,其中更多地讨论了 DI。
【讨论】:
除了其他帖子中提到的测试和设计问题外,还有单例和类加载器的问题。单例并不是每个 JVM 或应用程序真正的“单例”——它们是通过静态属性来实现的,这实际上意味着每个类都有一个。如果有多个类加载器 - 就像在大多数应用程序服务器中一样 - 每个单独的应用程序都会获得一个新的类加载器,甚至 EJB 中也会使用多个级别的类加载器。每个类加载器都会加载一个单例实例 - 这取决于您对单例执行的操作,可能不会产生您期望的结果。
【讨论】:
我很少使用单例。由于它们的性质(静态、全局对象),它们很难用于对代码进行单元测试。您最终需要进行一些同步或构建一些重新初始化机制,以便您可以获得每个单元测试的新版本。有些情况是有意义的——例如,一个全局配置类——但它们比刚接触单例的人所相信的要少得多。我知道我经历了一个阶段,我到处都看到了单例模式的应用。现在我尽可能避免它,并在遇到不必要的实现时通过重构代码来撤消它。
【讨论】: