为 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);
。我公司的开发人员习惯于看到静态方法,我认为这种方法对他们来说可能是可取的。
我的问题是:
- 这是解决我的问题的最佳方法吗?
- 我是否违反了任何最佳实践?
- 是否有描述 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,但调用代码仍然很难做到这一点。
我正在开发一个内部库,供我所在公司的其他开发人员使用。我正在应用 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);
。我公司的开发人员习惯于看到静态方法,我认为这种方法对他们来说可能是可取的。
我的问题是:
- 这是解决我的问题的最佳方法吗?
- 我是否违反了任何最佳实践?
- 是否有描述 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,但调用代码仍然很难做到这一点。