7

现在我们使用 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);
    }
}

现在的问题是我们最终创建了一个完整的lotta工厂类,人们并不总是知道使用它们(他们有时会自己新建它们)。像这样对类进行编码的最大缺点是什么:

public class EmailSender 
{
    EmailLogger logger = IoC.Resolve<ILogger>();
    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)
    {
    } 
}
4

3 回答 3

14

是的 - 这很糟糕。

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

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

  • 最后但,最不重要的是——你的 SL 代码散布在你的代码库中,这不是一件好事,因为当你想要改变某些东西时,你必须查看多个地方。
于 2009-10-23T08:15:28.007 回答
2

我能想到的最大原因(不只是查看一般服务定位器的问题)是这不是我作为您班级的用户所期望的。

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

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

于 2009-10-23T08:35:09.653 回答
1

我不太确定 Krzysztof 给出的这个强有力的“这很糟糕”的回答。我认为那里有一些权衡和偏好,而没有绝对地将它们归类为坏或好。

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

我认为使用服务定位器确实将您的依赖项隐藏在类中,而不是通过构造函数公开它们。我认为这很不方便,因为在没有配置服务定位器的情况下调用服务定位器之前,您不会知道您的类缺少某些东西。

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

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

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

菲尔

于 2009-11-04T18:02:34.600 回答