【问题标题】:How can I avoid/refactor bad singletons in practice?在实践中如何避免/重构糟糕的单例?
【发布时间】:2009-07-29 07:25:36
【问题描述】:

仔细阅读有关单例的事实(代码味道,而不是模式)后,我想知道:

如何重构我的代码以摆脱它们?

尽管几乎每个人都同意糟糕的单身人士很糟糕,但我找不到任何关于如何替换它们的实用建议。要么非常琐碎,要么非常困难。

我可以想到一些方法,但所有这些方法似乎都使我的代码非常臃肿。

例如,假设我有一个“全局”AppConfig 类,它包含有关产品的许可信息并描述用户可用的功能。

我能想到的:

  • 为我的项目的每个类创建一个公共基类,其中包含一个AppConfig 实例。 (不好:对于已经有基类的情况,例如表单,这是不可能的)
  • 使用setAppConfig 方法创建一个通用接口。
  • 创建一个可以创建AppConfig 实例的全局AppConfigFactory(错误:只能将问题转移到另一个类)
  • 将实例作为参数传递给每个需要它的方法。 (坏:代码膨胀)
  • ...

我能做什么?

编辑: 澄清:我在我的代码中发现了一个错误的单例。现在我想重构我的代码以删除它。我正在寻求有关如何实现这一目标的提示和一般想法。

【问题讨论】:

标签: design-patterns oop


【解决方案1】:

使用依赖注入和控制反转框架——这可能需要大量重构。然后,使用构造函数或属性依赖,请求“单例” - 理想情况下,你不要求整个事情,因为根据 Demeter 的原则,它应该只要求它真正需要的东西(在你的情况下是许可证信息)。

我试图区分单例(反模式伪装全局变量)和单例(意味着你只需要其中一个)。真正的单例在程序开始时(或在您的工厂中)创建一次,并传递给需要它的对象。

【讨论】:

  • 你只是不懂单例设计模式,你应该一直使用设计模式!!
  • 我真的不知道。你在讽刺吗?
  • 你能举一些这些框架的例子吗?我可以用谷歌搜索一些关键字吗?
  • 这取决于您的语言。对于 Java,有 Spring 和 GUICE。对于 .Net,有 Spring.Net。可能还有其他人。
  • 但是即使你确实使用了 IoC 框架,你仍然需要持有或访问一些主容器对象 - 它要么需要存储在静态类中,要么作为参数传递......所以我假设解决方案是 IoC 和 djna 建议的“主工厂”单音。
【解决方案2】:

你说

创建一个可以创建 AppConfig 实例的全局 AppConfigFactory(不好:只能将问题转移到另一个类)

在我看来,这实际上一点也不坏。客户的观点是他向工厂对象询问他应该使用的配置。他确实知道这是一个单身人士!单例性一下子被封装在工厂中。 [实际上,Factory 本身很可能最终成为一个 Singleton,但一切都必须引导,对吗?]

现在是否使用依赖注入技术来封装工厂访问是一种改进,基本是只有一个对象负责创建这些 AppConfig 对象,只有工厂知道是一个还是多个。

这让我想到了另一个宠物理论……没有像 1 这样的数字,当你开始时它看起来像一个单例,然后复杂性增加,你会发现你的应用程序的某些部分(例如) 使用一个配置,而另一部分使用不同的配置(例如,在版本之间的动态转换中)。工厂可以隐藏这种复杂性。

【讨论】:

    【解决方案3】:

    让类只有静态成员怎么样?就像,而不是这个(C# 代码):

    class AppConfig
    {
        public static readonly AppConfig Instance;
    
        private AppConfig() { }
        static AppConfig()
        {
            Instance = new AppConfig();
        }
    
        public string SomeConfigParam { get; set; }
    }
    

    这样做:

    static class AppConfig
    {
        public static string SomeConfigParam { get; set; }
    }
    

    只有当你需要传递实例时,单例才有意义——比如将参数传递给集合中的函数或值。 .NET 中的 System.DBNull 是一个很好的单例示例。如果它只是一个每个人都应该能够访问的全局数据存储,那么静态类可以让您的代码更短更简单。

    【讨论】:

    • 请注意,静态类会使您的代码更难测试。虽然如果它是一个简单的数据存储,那么这里就不是这样了。
    • 那不还是单身吗?
    • 不,它更像是全局变量,除了有一个很好的命名空间前缀。面对现实——在每个程序中都会有一些全局变量。需要在程序范围内访问的设置和数据。静态类/类成员是以 OOP 方式封装它们的最佳方式。
    • 全局变量也不错。只有当您将它们滥用于不需要在整个程序范围内可用的东西时,它们才是坏事。
    • 但这 * IS * 全局数据。不要试图假装没有全局数据。那是另一种代码味道/不好的做法/等等。当然,您可以使您的代码在任何地方都不会使用static 关键字。但这只会使其复杂化并使其更少可维护。与生活中的一切一样 - 避免极端。不要过度使用全局数据,也不要低估它。在真正有意义的地方使用它,在地狱里没有滚雪球的机会需要复制它(比如应用程序配置的示例)。
    【解决方案4】:

    如果对象在其整个生命周期中都需要 app-config,则通过 setAppConfig() 方法或构造函数传递 AppConfig。如果全局AppConfig 与对象之间的关联是有条件的(即仅在某些方法中需要),则将其作为额外参数传递。

    【讨论】:

      猜你喜欢
      • 2012-01-18
      • 2011-09-07
      • 1970-01-01
      • 1970-01-01
      • 2019-04-03
      • 1970-01-01
      • 1970-01-01
      • 2014-04-12
      • 1970-01-01
      相关资源
      最近更新 更多