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

时间:2009-10-21 09:57:15

标签: c# java dependency-injection inversion-of-control service-locator

现在我们使用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<ILogger>();
    internal EmailSender(string toEmail, string subject,String body)
    {.....} 
}

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

为什么我们不应该这样做,有什么大的原因吗?

编辑:经过一番思考后,我通过拥有一个私有构造函数来解决这个问题,并且通过嵌套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)
    {
    } 
}

3 个答案:

答案 0 :(得分:14)

是的 - 这很糟糕。

  • 为什么要在框架完成工作后编写所有代码?所有IoC.Resolve()调用都是 多余的,你不应该 写下来。
  • 另一个更重要的方面, 是你的组件绑定 你的服务定位器。

    您现在无法实例化它们 就像那样 - 你需要一个 完全设置服务定位器 每次你需要使用一个地方 成分

  • 最后但是,最少机器人 - 你的SL代码是 遍布你的代码库 这不是一件好事,因为 当你想改变什么时, 你必须在多个地方看。

答案 1 :(得分:2)

我能想到的最大原因(不仅仅是查看服务定位器的问题)是因为我不是我班级用户expect

元讨论:
一些DI框架(例如Guice)将build the factory for you

有些人advocate将“新手”与“注射”分开。

答案 2 :(得分:1)

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

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

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

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

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

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

菲尔