【问题标题】:What is a decent way of injecting a dependency to a singleton class?将依赖项注入单例类的一种不错的方法是什么?
【发布时间】:2011-05-26 07:37:50
【问题描述】:

我有一个依赖于另一个类的单例,因此我想注入此依赖项以使其可单元测试。由于无法在单例上使用构造函数注入,我想我应该改用 setter 注入,但我真的不喜欢它,因为在某些时候有人(可能是我自己)会忘记调用 setter。您当然可以将依赖对象注入到单例的 getInstance 方法中,但这也很丑陋。有没有更好的方法来解决这个问题(不使用 IoC 容器之类的工具)?

public class Singleton {
    private ISomeDependency _dependency;
    private static final Singleton INSTANCE = new Singleton();
    private Singleton() {
    }
    public static Singleton getInstance() {
       return INSTANCE;
    }
    ...
}

【问题讨论】:

  • 好问题。但我确实希望你能接受那些让你远离上述你想做的事情的答案。

标签: design-patterns language-agnostic dependency-injection singleton


【解决方案1】:

单例设计模式是一个很大的反模式,因为没有办法在单例中注入依赖。然而,这并不意味着在应用程序的生命周期内无法拥有单个实例,但您不应该为此使用单例设计模式。尤其是在使用依赖注入的时候,没有理由使用这种设计模式。

只需将该类型的一个实例作为构造函数注入依赖它的类型中,就完成了。这使您可以在应用程序的顶部构建对象图并控制那里的生活方式并将依赖项注入该实例。

该类的设计应该与任何其他类一样:

public class SomeService : ISomeService 
{
    private ISomeDependency _dependency;

    public ISomeService(ISomeDependency dependency)
    {
        _dependency = dependency;
    }

    // ISomeService members here
}

依赖注入框架将允许您轻松定义任何类型的生活方式。但是,对于简单的应用程序,您仍然可以手动执行此操作。

【讨论】:

  • 感谢您的回答。但是如果不使用单例模式,我不知道如何绝对确定只有一个类的实例?由于构造函数必须是公开的,因此任何人都可以在任何地方创建它的实例。
  • @Christian:这就是仅在组合根(即应用程序顶部)实例化依赖项的全部意义,例如 Main()。当然你可以在其他地方实例化一个依赖,但是如果你养成从不这样做的习惯(除了来自工厂等),那么世界将是一个居住环境更环保 :) 例如,您可以随时 ::memset() 任意内存在 C 应用程序中进行垃圾处理,但这并不正确。
  • @Johann:是的,我只是在寻找一种方法来最大限度地降低错误使用解决方案的风险并最大限度地提高正确使用它的机会。在现实世界的项目中,总会有人以非预期的方式使用代码。在建议的解决方案中,您绝对可以肯定有人会在某个时候创建​​自己的类实例,该实例应该充当单例。
  • @Christian:那是更大问题的征兆。如果代码审查正在工作并且团队有一个商定的整体设计方法,那么服务依赖项没有自己实例化也就不足为奇了。也就是说,如果首先实际使用 DI。
  • @Christian:我同意 Johan 关于使用代码审查和编写高质量代码的观点。如果这还不够,您可以使用 NDepend 或 XDepend 等工具来验证您的软件是否符合您的规则。否则,定义一个单独的组件(程序集/包),只有组合根链接到该组件。这样,具体的实现根本不存在,只有接口。我个人只会在所有其他选项都失败时才这样做。
【解决方案2】:

您可以使用依赖注入容器(如Spring.NetMS Unity 等)来获取依赖的具体类型。

public class Singleton {
private ISomeDependency _dependency = Container.Resolve<ISomeDependency>();
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
   return INSTANCE;
}
...
}

现在您可以在单例之外进行控制了。

【讨论】:

  • 谢谢。但是,我想知道在不使用任何 IoC 容器的情况下解决此问题的首选方法是什么。我将编辑我的问题。
  • 这样你就会对你的容器有一个硬依赖,这使得编写单元测试变得很困难。您应该始终更喜欢使用依赖注入模式而不是服务定位器模式。
猜你喜欢
  • 2018-06-26
  • 2018-01-15
  • 2015-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-24
相关资源
最近更新 更多