为DI不可知库

时间:2018-03-22 13:20:52

标签: c# dependency-injection webforms ioc-container legacy-code

我正在开发一个内部库,供我正在为之工作的公司中的其他开发人员使用。我正在应用SOLID模式并遵循Dependency Inject (DI) “friendly” library中描述的最佳实践。

我的最终用户将是不同应用程序的开发人员。其中一些是没有DI的复杂遗留应用程序,另一些是具有DI和TDD的新应用程序。

现在,我试图弄清楚如何从没有实现DI的遗留ASP.NET Webforms应用程序中调用这个DI友好库,显然,我无法修改250多个aspx页面以支持构造函数注入因为它超出了我的项目范围。 (Yes, I have read Introducing an IoC Container to Legacy Code

我的一个想法是为Common Service Locator创建一个静态全局包装器,以自动解决整个应用程序中的依赖关系:

public static class GlobalResolver
{
    public static T Resolve<T>()
    {
        return ServiceLocator.Current.GetInstance<T>();
    }
}

这种方法的好处是我可以在我的合成根中使用任何IoC库(我目前使用Unity)。我会像这样使用这个GlobalResolver:

protected void OnClick(object sender, EventArgs e)
{
    IMailMessage message = MessageFactory.Create("Jack.Daniels@jjj.com", "John.Doe@jjj.com", "subject", "Body", true, MailPriority.High);
    GlobalResolver.Resolve<IMailer>().SendMail(message);
}

我喜欢这种方法,我觉得它很干净,但我公司的新手开发人员可能会对这个GlobalResolver.Resolve<IMailer>行感到困惑,所以我试图看看是否有替代方案。

我想到的一件事是这样的:

public static class CommonCatalog
{
    public static IMailer Mailer => ServiceLocator.Current.GetInstance<IMailer>();
    public static IMailMessageFactory MessageFactory => ServiceLocator.Current.GetInstance<IMailMessageFactory>();
    public static IFtpSecureClientFactory FTPClientFactory => ServiceLocator.Current.GetInstance<IFtpSecureClientFactory>();

    // And so on...
}

简单地使用它:CommonCatalog.Mailer.SendMail(message);。我公司的开发人员习惯于看静态方法,我认为这种方法对他们来说可能是理想的。

我的问题是:

  1. 这是我问题的最佳解决方案吗?
  2. 我是否违反了任何最佳做法?
  3. 是否存在描述CommonCatalog类的设计模式?它是“门面”还是“代理”?
  4. TLDR:我公司的开发人员喜欢使用静态方法,但静态方法与DI和SOLID实践不兼容。有没有办法诱骗人们认为他们正在使用静态方法,但在幕后调用DI代码?

1 个答案:

答案 0 :(得分:1)

如果你想避开Service Locator anti-pattern(你应该这样做,因为 - 它是反模式),那么带GlobalResolver的第一个选项是不可能的,因为it's definitely a Service Locator

服务目录更接近我在Facades的扩展文章中推荐的DI-friendly libraries,尽管我通常不喜欢没有聚合的对象目录。当我不知道如何命名对象时,它总是让我感到不舒服,像CommonCatalog这样的名字似乎太缺乏意义。

相反,我更倾向于使用文章中描述的Fluent Builder模式制作基于实例的Facade,因为当您在线下发现需要添加各种选项时,它会更灵活。切换到立面。

但是,如果绝对必须,可以为每个Facade添加静态方法。像这样:

public static class Mailer
{
    public static IMailer Default
    {
        get { return new MailerBuilder().Create(); }
    }
}

如果实例可以想象具有单身生命周期,则可以使用Singleton design pattern,如下一个示例所示。

你可以用同样的方式实现一个默认的MessageFactory,但是这里改为使用Singleton设计模式:

public static class MailMessageFactory
{
    public static IMailMessageFactory Default { get } =
        new MailMessageFactoryBuilder().Create();
}

请注意,这并没有使用服务定位器来实现。

要明确的是,背后这样的外观可以很容易地根据SOLID原则实现,但是调用代码仍然很难做到这一点。