依赖注入和单例设计模式

Sys*_*min 75 oop design-patterns dependency-injection

我们如何确定何时使用依赖注入或单例模式.我在很多网站上都读到过"使用依赖注入超过单一模式"的网站.但我不确定我是否完全赞同他们.对于我的中小型项目,我肯定会看到单例模式的使用直截了当.

例如Logger.我可以使用Logger.GetInstance().Log(...) But,而不是这个,为什么我需要使用logger的实例注入我创建的每个类?

Bry*_*tts 84

单身人士就像共产主义:他们在纸上听起来都很棒,但在实践中却有问题.

单例模式过分强调访问对象的难易程度.它通过要求每个消费者使用AppDomain范围的对象完全避开上下文,不为不同的实现留下任何选项.它将基础设施知识嵌入到您的课程中(对呼叫GetInstance()),同时添加完全零的表达能力.它实际上减少了你的表达能力,因为你不能改变一个类所使用的实现而不改变所有类的实现.您根本无法添加一次性功能.

此外,当类Foo依赖时Logger.GetInstance(),Foo有效地将其依赖性隐藏在消费者之外.这意味着你无法完全理解Foo或使用它,除非你阅读它的来源并揭示它依赖的事实Logger.如果您没有源代码,则会限制您理解和有效使用您依赖的代码的程度.

使用静态属性/方法实现的单例模式只不过是实现基础结构的黑客攻击.它以无数种方式限制了你,同时对替代方案没有任何明显的好处.您可以随意使用它,但由于有可行的替代方案可以促进更好的设计,因此绝不应该是推荐的做法.

  • @BryanWatts所说的,在正常的中等规模应用中,Singletons仍然更快,更不容易出错.Usualy我没有多个(自定义)Logger的可能实现,所以为什么**我需要:1.为此创建一个接口并在每次需要添加/更改公共成员时更改它2.维护一个DI配置3隐藏事实,那就是这种类型的系统整体4.绑定自己引起的过早分离的担忧了严格的功能的单个对象. (7认同)
  • 单例和依赖注入的使用并不相互排斥.单例可以实现接口,因此可以用于满足对另一个类的依赖.它是单例的事实并不强制每个消费者通过它的"GetInstance"方法/属性获得引用. (4认同)
  • @UriAbramson:够公平的。您是否至少同意在测试期间交换实现以进行隔离很重要? (2认同)

Boz*_*zho 56

如果要验证测试中记录的内容,则需要依赖注入.此外,记录器很少是单例 - 通常每个类都有一个记录器.

观看面向对象设计的可演示性演示文稿,您将看到为什么单身人士不好.

单例的问题在于它们代表了一个难以预测的全局状态,尤其是在测试中.

请记住,一个对象可以是事实上的单例,但仍然可以通过依赖注入而不是通过via获得Singleton.getInstance().

我只是列出了Misko Hevery在他的演讲中提出的一些重要观点.看着它后,你将获得关于为什么它是最好有一个对象定义全透视什么的依赖关系,但不能定义一个方法如何创建它们.


Pét*_*rök 17

其他人已经很好地解释了单身人士的问题.我想添加一个关于Logger特定情况的说明.我同意你那它通常是不访问一个Logger(或根记录器,更确切地说),为单一的问题,通过一个静态getInstance()getRootLogger()方法.(除非你想看看你正在测试的课程记录了什么 - 但根据我的经验,我几乎无法回想起有必要的情况.再说一次,对于其他人来说,这可能是一个更紧迫的问题).

IMO通常单独记录器不用担心,因为它不包含与您正在测试的类相关的任何状态.也就是说,记录器的状态(及其可能的变化)对测试类的状态没有任何影响.因此,它不会使您的单元测试更加困难.

另一种方法是通过构造函数将记录器注入(几乎)应用程序中的每个类.为了接口的一致性,即使所讨论的类目前没有记录任何内容,也应该注入它 - 另一种选择是当你发现某个时候现在你需要从这个类中记录一些东西时,你需要一个记录器,因此你需要为DI添加一个构造函数参数,打破所有客户端代码.我不喜欢这两种选择,我觉得使用DI进行测井会使我的生活变得复杂,以符合理论规则,没有任何具体的好处.

所以我的底线是:一个几乎普遍使用的类,但不包含与你的app相关的状态,可以安全地实现为Singleton.

  • 如果我错了,请纠正我,但单身人士将是LogFactory,而不是记录器.另外,LogFactory很可能是apache commons或Slf4j logging facade.因此,无论如何切换日志记录实现都是无痛的 使用DI注入LogFactory的真正痛苦不在于您现在必须转到applicationContext来创建应用程序中的每个实例吗? (5认同)
  • @kyoryu,我在谈论"通常"的情况,IMHO暗示使用(事实上的)标准日志框架.(通常可通过属性/ XML文件配置btw.)当然也有例外 - 一如既往.如果我知道我的应用程序在这方面非常出色,我确实不会使用Singleton.但过度工程说"这可能在某个时候有用"几乎总是浪费精力. (3认同)

Sco*_*ein 9

它主要是,但不是关于测试.Singltons很受欢迎,因为它很容易消耗它们,但单身人士有许多缺点.

  • 很难测试.这意味着我如何确保记录器做正确的事情.
  • 难以测试.这意味着如果我正在测试使用记录器的代码,但它不是我测试的重点,我仍然需要确保我的测试环境支持记录器
  • 有时你不需要唱歌,但更灵活

DI让您轻松使用依赖类 - 只需将其放入构造函数args中,系统就可以为您提供 - 同时为您提供测试和构建灵活性.


kyo*_*ryu 5

大约唯一一次您应该使用 Singleton 而不是依赖注入的是 Singleton 表示不可变值,例如 List.Empty 等(假设不可变列表)。

对 Singleton 的直觉检查应该是“如果这是一个全局变量而不是 Singleton,我可以吗?” 如果没有,您正在使用单例模式来掩盖全局变量,并且应该考虑采用不同的方法。