使用servicelocation而不是构造函数注入来避免编写工厂类的负载是不好的

现在我们使用DI / IOC,当我们需要将额外的参数传递给构造函数时,我们使用工厂类,例如

public class EmailSender { internal EmailSender(string toEmail, string subject,String body, ILogger emailLogger) {.....} } public class EmailSenderFactory { ILogger emailLogger; public EmailSenderFactory(ILogger emailLogger) { this.emailLogger = emailLogger; } public EmailSender Create(string toEmail, string subject, string body) { return new EmailSender(toEmail, subject, body, emailLogger); } } 

现在问题在于我们最终创建了一个完整的工厂类,并且人们并不总是知道使用它们(他们有时会自己创建它们)。 编写类的最大负面因素是:

 public class EmailSender { EmailLogger logger = IoC.Resolve(); internal EmailSender(string toEmail, string subject,String body) {.....} } 

Pro:我们现在可以安全地使用构造函数而无需工厂类Con:我们必须引用Service Locator(我不担心可测试性,它很容易使用模拟容器作为容器的支持服务)。

是否有一些重要的原因,为什么我们不应该这样做?

编辑:经过一番思考,我通过拥有一个私有构造函数,并通过嵌套Factory类,我可以将实现和工厂保持在一起,并防止人们不正确地创建类,所以这个问题已经变得有点没有实际意义了。 所有关于SL的点都很脏,当然是正确的,所以下面的解决方案让我高兴:

 public class EmailSender { public class Factory { ILogger emailLogger; public Factory(ILogger emailLogger) { this.emailLogger = emailLogger; } public EmailSender Create(string toEmail, string subject, string body) { return new EmailSender(toEmail, subject, body, emailLogger); } } private EmailSender(string toEmail, string subject,String body, ILogger emailLogger) { } } 

是的 – 这很糟糕。

  • 为什么在可以让框架完成工作时编写所有代码? 所有IoC.Resolve()调用都是多余的,您不必编写它们。
  • 另一个更重要的方面是您的组件与服务定位器绑定。

    您现在无法像这样实例化它们 – 每次需要使用组件时,您需要一个完全设置的服务定位器。

  • 最后但是,机器人最少 – 你的SL代码遍布你的代码库,这不是一件好事,因为当你想要改变某些东西时,你必须在多个地方寻找。

我能想到的最大原因(不仅仅是查看服务定位器的问题)是因为我不是我class级用户所期望的 。

元讨论:
一些DI框架(例如Guice )将为您构建工厂 。

有些人主张将“新的”与“可注射的”分开。

我不太确定Krzysztof给出的这个强烈的“这是坏的”答案。 我认为在那里有一些权衡和偏好而没有绝对将它们归类为坏或好。

  • 我不认为编写IoC.Resolve()调用比编写注入机制的特定构造函数或属性更多余。
  • 这个,我必须同意你绑定到服务定位器,你必须在实例化一个类之前进行设置。 然而:
    • 您可以使用更具体的接口隔离服务定位器。 从而减少与系统的每个服务的巨大服务定位器的耦合
    • 是的,如果你使用DI机制,你将删除所有那些IoC.Resolve(),但你仍然需要使用一种容器来实例化你的“主要”服务。 DI必须拦截那些电话,不是吗?
    • 您的服务定位器可以(应该?)是“可自动配置的”,或者至少非常容易设置。
  • 请参阅上面的“使用更具体的接口隔离服务定位器……”。

我认为使用服务定位器确实隐藏了类中的依赖项,而不是通过构造函数公开它们。 在我看来这很不方便,因为在没有配置服务定位器的情况下,你不会知道你的类缺少了什么。

但DI的东西并没有摆脱那种代码的黑暗。 当你使用DI时,理解那些依赖关系是如何在你的构造函数中“出现”(DI魔术)真的不明显。 通过使用SL,您至少可以看到这些依赖项的来源。

但是,当测试一个在她的构造函数上暴露这些依赖关系的类时,你(几乎)不能错过它。 使用服务定位器的情况并非如此。

我不是说Krzysztof错了,因为我最同意他。 但我很确定使用服务定位器不一定是一个糟糕的“设计”,当然也不是简单的坏。

菲尔