【问题标题】:Singleton and static classes case study单例和静态类案例研究
【发布时间】:2012-01-23 07:26:33
【问题描述】:

已经是asked 单例和静态类有什么区别。但是,知道了区别,每次需要选择的时候我还是会感到困惑。

所以我为我自己定义了两种不同的情况——如果应该只有这个类的一个实例(很少见)和所有服务类的静态类(这经常发生)。

例如,在我的应用程序中,我需要存储消息(我有一个可序列化的类 Message),将它们写入文件,从文件中读取并在运行时访问。我看不出有什么理由在这里使用单例,静态类就可以了。唯一的静态类是 MessageStorage,它具有 3 个功能 - 读取、写入和 getMessages 以及一个静态私有消息数组列表。

这种方法合理吗?如果不合理,它的问题是什么?

【问题讨论】:

  • 全局状态/静态通常被认为是不好的形式,这就是为什么单例通常被认为是antipattern。 “好的做法”是静态类和单例应该只用于没有状态的辅助方法(例如Math.sqrt(4))。
  • 那么在这种情况下你会怎么做呢?
  • 在需要存储状态的情况下,您只需对对象的单个引用。这样的单例模式不能保证它,但它会位于需要该信息的任何东西都可以全局访问的位置。它将达到相同的效果,但正如我在帖子中所说,不同之处在于可扩展性和可变性随着需求的变化而得到改进。创建一个类一旦实现了单点目标,您需要做的就是在全局可找到的地方引用它。
  • 如果您进行单元测试(这有助于说明为什么静态方法很烦人),您会注意到这并不容易或不可能?模拟测试静态方法。如果单元测试受到阻碍(即使您对此不感兴趣),这是一个巨大的警告信号,您的代码不够健壮并且无法很好地处理需求更改。

标签: java singleton static-class


【解决方案1】:

在 Java 中使用“单例”的两个主要原因是:

1) 因此,一些存储可以与其他“静态”类相关联。

2) 对于给定服务的不同子类(或接口实现),您可能有多个“单例”,并且单例作为识别要使用的特定服务的一种方式传递。

当然,除了#1,您可以使用“静态”类的静态字段来包含数据,但拥有主题类的单个实例通常更方便(并且在许多情况下更有效)包含数据,而不是作为其他类实例的多个静态成员。

并且,关于 #2,在 JDK 中有许多情况下,一个类实际上以定义“常量”的名义实现了多个“单例”。 (例如,java.awt.font.TextAttribute。)

一般来说,Java 中使用单例的动机比基于 C 的语言要少,因为 Java 确实实现了真正的类相关(和受类保护)静态数据,而不是隐约伪装(如果完全伪装的话)的全局C 语言中使用的静态变量,因此可以简单地在一个类中拥有多个静态字段,而不需要有一个“中心”对象来包含 C 语言中的字段。

【讨论】:

    【解决方案2】:

    理想的设计:-)

    • MessageStore 应该是一个接口
    • MessageStoreFactory 应该是一个带有 getMessageStore() 方法的单例(如果 getMessageStore() 每次都返回相同的消息存储,那不是问题,而且您有自己的单例)。
    • 然后您可以拥有多个 MessageStore 实现,例如 FileMessageStore、JDBCMessageStore、SubversionMessageStore 等...
    • 最重要的是,您可以使用 MockMessageStore 来模拟消息存储,并能够独立于消息存储测试依赖于消息存储的组件(并隔离任何故障)。例如,如果您正在测试 MessageView 并收到错误,您可以确定错误在 MessageView 而不是在静态 MessageStore 中,因为 MockMessageStore 是正确的。

    这就是现在很酷的孩子们正在做的事情,无论如何......以及依赖注入而不是工厂,但一步一步......

    【讨论】:

      【解决方案3】:

      这里要真正理解的是c#/java有不同的内存组。

      您的所有类都将被加载到称为类加载器内存的一段内存中。如果您制作的东西是静态的,它会保留在类加载器内存中。它可以从那里使用,但只能有 1 个实例。没有用于类加载器内存的垃圾收集器,在应用程序的整个生命周期内,一切都将保留在那里。

      创建一个类的实例,无论它是否为单例,都意味着完成了内存复制,从类加载器内存中复制您的类并将其复制到存储实例的区域。实例可以被垃圾回收。

      单例和静态类选项之间的实际决策点是:

      1) 你想要继承,还是你的类的接口(如果你想要单例)
      2) 强制您的类具有与您的应用程序相同的生命周期是否有意义(即您必须手动清除该类或编写一个方法来执行它,这会不必要地增加代码,从而降低可维护性)。 (如果没有,那么你想要单例)
      3)您的应用程序是否需要可扩展性和更改潜力。如今,单例通常被认为是一种反模式,IF 通过静态属性实现。这样做的原因是你投资了基础设施,比如在你的类上公开一个静态实例属性,这可能完全不是你未来想要的,因为你的单窗口应用程序可能突然变成多窗口,你需要重写代码。 (重写代码表明设计不佳,尤其是在其核心基础架构的情况下)。

      作为一般经验法则,我会推荐以下内容:

      • 存在类范围变量的任何类都应该是单例(因为需要潜在地扩展它)。
      • 每个方法独立存在的任何类都应该是静态的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-06-02
        • 2020-11-08
        • 1970-01-01
        • 2020-10-25
        • 1970-01-01
        • 1970-01-01
        • 2010-09-25
        • 2019-07-12
        相关资源
        最近更新 更多