【问题标题】:Singleton Design Pattern: Pitfalls [closed]单例设计模式:陷阱[关闭]
【发布时间】:2009-09-19 11:43:08
【问题描述】:

不确定使用严格的全局状态实现是否会失败。单例何时不适合应用程序?

【问题讨论】:

  • Here 是关于单例作为反模式的好读物。

标签: java design-patterns singleton


【解决方案1】:

如果您正在进行单元测试,单例通常是个坏主意,不进行单元测试(或 BDD 或验收测试)通常是个坏主意。

使对象具有全局状态意味着您编写的涉及这些对象的单元测试将彼此隔离和脱节。相反,您将不得不担心为每个测试重置状态,相信我……这永远不会 100% 完成。如果您不重置全局状态,那么您会开始在测试中遇到非常奇怪且难以调试的错误,这些错误会浪费时间。

全局状态还会增加代码的耦合度,使其难以重构。

理想的方法是使用 IoC/DI 容器(Spring、Guice 等)来请求对象。这些容器通常具有使对象显示为“单例”的方法,但它们也有根据情况修改该行为的方法(即单元测试与您的域代码)。

这当然取决于您问题的大小。如果你正在拼凑一个 4-class 测试台来尝试一些东西,那么继续使用 Singleton。但是,一旦该项目开始运作并变得更大、更复杂,那么就将 Singleton 重构出来。

【讨论】:

  • 那么您的意思是单例从不有资格在实际项目中使用(无论用例如何,在实际项目中发现单例都会自动等同于代码异味)?
  • 那么请不要闻自己的气味。您的回答表明单例仅用于“初始阶段”代码,并且应该在所有变得更大的成熟项目中排除。那么你的意思是单例永远不适合在这些项目中使用,还是有有效的用例?
  • 很好。无论用例和项目大小如何,实际项目中的单例都是一种代码味道。使用 DI 模式(如果您选择使用特定容器)完全不需要它们,而单例会导致解决方案更紧密耦合且更难长期维护。
【解决方案2】:

Google Tech Talks 前段时间有一个关于Global State and Singletons 的精彩演示。静态单例模式是邪恶的,因为它会导致不必要的副作用并使代码无法测试。静态单例是全局变量的面向对象版本。

解决方案是只创建一个对象的实例,然后通过依赖注入将其传递给它的用户。 DI 框架,例如Guice,使定义良好的单例变得容易(在 Guice 中,只需使用 @Singleton 注释一个类)。有一个类似的 Tech Talk,称为 Don't Look For Things!,其中更多地讨论了 DI。

【讨论】:

    【解决方案3】:

    除了其他帖子中提到的测试和设计问题外,还有单例和类加载器的问题。单例并不是每个 JVM 或应用程序真正的“单例”——它们是通过静态属性来实现的,这实际上意味着每个类都有一个。如果有多个类加载器 - 就像在大多数应用程序服务器中一样 - 每个单独的应用程序都会获得一个新的类加载器,甚至 EJB 中也会使用多个级别的类加载器。每个类加载器都会加载一个单例实例 - 这取决于您对单例执行的操作,可能不会产生您期望的结果。

    【讨论】:

    • @Nate,这听起来很有趣,你能给我一个具体的例子吗?
    • 您的声明是否有权威来源?为什么单独的 JRE 实例不使用单独的单例实例?
    • @Pacerier oracle.com/technetwork/articles/java/singleton-1577166.html 使用单独的单例实例的单独 JRE 是预期的情况 - 问题是单个 JRE 可以有多个类加载器,因此可以有多个单例实例。
    【解决方案4】:

    我很少使用单例。由于它们的性质(静态、全局对象),它们很难用于对代码进行单元测试。您最终需要进行一些同步或构建一些重新初始化机制,以便您可以获得每个单元测试的新版本。有些情况是有意义的——例如,一个全局配置类——但它们比刚接触单例的人所相信的要少得多。我知道我经历了一个阶段,我到处都看到了单例模式的应用。现在我尽可能避免它,并在遇到不必要的实现时通过重构代码来撤消它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-19
      • 2010-09-14
      • 1970-01-01
      • 1970-01-01
      • 2010-11-16
      • 2010-10-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多