【问题标题】:What's Alternative to Singleton什么是单例的替代品
【发布时间】:2010-11-21 00:02:28
【问题描述】:

我们有一个类来保存应用程序的配置信息。它曾经是一个单身人士。经过一些架构审查后,我们被告知要删除单例。我们确实看到了在单元测试中不使用单例的一些好处,因为我们可以同时测试不同的配置。

如果没有单例,我们必须在代码中到处传递实例。它变得如此混乱,所以我们编写了一个单例包装器。现在我们将相同的代码移植到 PHP 和 .NET,我想知道是否有更好的模式可以用于配置对象。

【问题讨论】:

    标签: java design-patterns singleton


    【解决方案1】:

    仅包含静态方法和字段的类是否可行?我不确定您的具体情况,但可能值得研究。

    【讨论】:

    • 如果类是无状态的,应该是静态类。
    • 它在 C++ 中 - 该模式被称为 Monostate。
    【解决方案2】:

    最好的方法是改用工厂模式。当您构造类的新实例(在工厂中)时,您可以将“全局”数据插入到新构造的对象中,作为对单个实例(存储在工厂类中)的引用或通过复制相关数据到新对象中。

    然后,您的所有对象都将包含曾经存在于单例中的数据。我认为总体上没有太大区别,但它可以使您的代码更易于阅读。

    【讨论】:

    • 我不同意“最佳方式”的说法,但 +1 是一个不错的选择。
    • 这种方法的问题是每个新对象都包含(或引用)可能是大量数据的数据。 var_dump() 在任何这些包含 gob 的对象上都会很快产生大量列表,其中包含 recursion 警告。它非常丑陋,效率不高,而且让事情看起来很混乱。但是,我个人还没有找到更好的方法。我确实将“工厂”方法转变为使用 __construct() 来引用全局。然而,为了避免可怕的单身人士,感觉一切都向后弯曲......
    • @EastGhostCom:我们不妨使用单例,不要再为难自己了:)
    【解决方案3】:

    我可能会在这里说明显而易见的问题,但是您是否有理由不能使用诸如 SpringGuice 之类的依赖注入框架? (我相信 Spring 现在也可用于 .NET)。

    这样,框架可以保存配置对象的单个副本,并且您的 bean(服务、DAO 等)不必担心查找它。

    这是我通常采取的方法!

    【讨论】:

      【解决方案4】:

      也许也不是很干净,但您可以将想要更改的信息位传递给创建单例的方法——而不是使用

      public static Singleton getInstance() {
          if(singleton != null)
              createSingleton();
              return singleton;
          }
      }
      

      您可以在应用程序启动时(以及在单元测试的 setUp-Methods 中)直接调用 createSingleton(Information info)

      【讨论】:

        【解决方案5】:

        如果你使用Spring Framework,你可以创建一个普通的bean。默认情况下(或者如果您显式设置scope="singleton")只创建一个bean 实例,并且每次在依赖项中使用该bean 或通过getBean() 检索该bean 时都会返回该实例。

        您可以获得单实例的优势,而无需耦合单例模式。

        【讨论】:

        • 哦,讽刺的是 - 使用(单例)Spring bean 来代替您的单例...
        【解决方案6】:

        Google Testing blog 有一系列关于避免 Singleton 的条目(为了创建可测试的代码)。也许这可以帮助你:

        上一篇文章详细解释了如何将新对象的创建移动到工厂中,这样可以避免使用单例。值得一读。

        简而言之,我们将所有新操作员转移到工厂。 我们将所有具有相似生命周期的对象分组到一个工厂中。

        【讨论】:

        • *** 使用依赖注入避免单例
        • 这些文章和谷歌C++编程标准一样好!
        • 嗯,不是真的。例如,“不要使用静态方法”的建议直接违背了 Scott Meyers / Herb Sutters 的最小接口原则。有一些有用的建议,但它们缺乏多种思想的贡献。
        • @FrankS 为什么你改变了链接的顺序?起初它的时间顺序很好。
        • @Cawas 其实不知道,那是四年多以前的事了,所以我想我当时有一些原因:-)
        【解决方案7】:

        取决于正在使用的工具/框架等。 使用依赖注入/ioc 工具,通常仍然可以通过让 di/ioc 容器对所需的类(例如 IConfigSettings 接口)使用单例行为来获得单例性能/优化,方法是只创建类的一个实例。这仍然可以代替测试

        或者,可以使用工厂来创建类并在每次请求时返回相同的实例 - 但为了测试它可能会返回一个存根/模拟版本

        【讨论】:

          【解决方案8】:

          另一种方法是传入你需要的东西,而不是向对象索要东西。

          【讨论】:

            【解决方案9】:

            查看将配置作为回调接口的可能性。 因此,您的配置敏感代码将如下所示:

            MyReuseCode.Configure(IConfiguration)
            

            系统初始化代码如下:

            Library.init(MyIConfigurationImpl)
            

            【讨论】:

              【解决方案10】:

              不要对单个配置对象累积责任,因为它会以一个既难以理解又脆弱的非常大的对象结束。

              例如,如果您需要一个特定类的另一个参数,您可以更改Configuration 对象,然后重新编译所有使用它的类。这有点问题。

              尝试重构您的代码以避免一个通用的、全局的和大的Configuration 对象。只将必需的参数传递给客户端类:

              class Server {
              
                  int port;
              
                  Server(Configuration config) {
                      this.port = config.getServerPort();
                  } 
              
              }
              

              应该重构为:

               class Server {
              
                  public Server(int port) {
                     this.port = port;
                  }
               }
              

              依赖注入框架在这里会有很大帮助,但不是必须的。

              【讨论】:

              • 是的,这真的很好。我以前做过。我的大配置对象实现了 MailServiceConf、ServerConf 等接口,而依赖注入框架将配置传递给类,因此我的类不依赖于大配置对象。
              【解决方案11】:

              您可以使用依赖注入框架来减轻传递配置对象的痛苦。一个不错的是ninject,它的优点是使用代码而不是xml。

              【讨论】:

                【解决方案12】:

                您可以使用静态方法完成与单例相同的行为。 Steve yegge 在this 帖子中解释得很好。

                【讨论】:

                • 其实这篇文章还是不错的,并没有说应该改用静态方法。相反,他说静态方法也只是单例,最后他建议使用工厂方法模式:“我最后说,如果您仍然觉得需要使用单例对象,请考虑使用工厂方法模式。 ...”
                【解决方案13】:

                单例并不邪恶,但设计模式有缺陷。我有一个类,我只想在运行时创建它的单个实例,但想在单元测试期间创建多个隔离实例以确保确定性结果。

                使用 Spring 等的 DI 是一个很好的选择,但不是唯一的选择。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2019-02-22
                  • 2011-05-07
                  • 2012-04-24
                  • 2019-12-03
                  • 2010-09-17
                  • 2012-03-27
                  • 2012-09-30
                  • 2010-10-20
                  相关资源
                  最近更新 更多