为 DI 不可知库创建对象 "Catalog"

Creating object "Catalog" for DI agnostic library

我正在开发一个内部库,供我所在公司的其他开发人员使用。我正在应用 SOLID 模式并遵循 Dependency Inject (DI) “friendly” library.

中描述的最佳实践

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

现在,我正试图弄清楚如何从遗留的 ASP.NET Webforms 应用程序中调用这个 DI 友好的库,其中没有实现 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 的设计模式 class?是 "Facade" 还是 "Proxy"?

TLDR:我公司的开发人员喜欢使用静态方法,但是静态方法与 DI 和 SOLID 实践不兼容。有什么方法可以骗人以为自己用的是静态方法,但背后调用的是DI代码?

如果你想避免 Service Locator anti-pattern (which you should, because - it's an anti-pattern), then the first option with a GlobalResolver is out of the question, because it's definitely a Service Locator.

服务目录更接近 Facades I recommend in my expanded article on DI-friendly libraries,尽管我通常不喜欢没有对象的聚合目录。当我不知道如何命名对象时总是让我很不舒服,而且像CommonCatalog这样的名字似乎太没有意义了。

相反,我更喜欢使用文章中描述的 Fluent Builder 模式制作 instance-based Facades,因为当您发现需要添加各种选项和开关到外观。

如果绝对必要,您可以为每个 Facade 添加一个静态方法。像这样:

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

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

您可以用相同的方式实现默认的 MessageFactory,但这里使用单例设计模式:

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

请注意,这也不使用服务定位器来实现。

尽管如此,背后 可以根据 SOLID 原则轻松实现此类 Facades,但调用代码仍然很难做到这一点。