没有服务定位器的工厂模式

我目前只是在努力编写一个不依赖服务位置的工厂类。

我能想到的唯一其他选择是使用构造函数注入来注入所有可能的实例,但这可能会导致意外,因为类是通过引用传递的。 一旦可能的提供商数量增加,它也可能会变得昂贵和混乱。

提供者本身是完全复杂的类,它们有自己的依赖关系,因此手动构造是不可能的。

更新了服务位置示例:

public class ProviderFactory : IProviderFactory { private readonly IProviderConfigurationService _providerConfigurationService; public enum SearchType { Foo, Bar } public ProviderFactory(IProviderConfigurationService providerConfigurationService) { _providerConfigurationService = providerConfigurationService; } public Collection GetProviderInstances(SearchType searchType) { // Provider configuration service will read a XML/DB store to retrieve list of search providers applicable for a search type var providerList = _providerConfigurationService.GetProviderList(searchType); return new Collection(providerList.ForEach(x=> ServiceLocator.GetInstance(typeof(x))).ToList()) ; } } 

我的其他选择是什么? 我目前正在使用Unity for DI。

另一种方法是将Func传递给构造函数并通过容器实现该函数:

 unity.RegisterInstance>(t => unity.Resolve(t)) 

然后在你的class上:

 public ProviderFactory(Func createFunc, IProviderConfigurationService pcs) { _createFunc = createFunc; } public Collection GetProviderInstances(SearchType searchType) { var providerList = _providerConfigurationService.GetProviderList(searchType); return new Collection(providerList.Select(_createFunc).ToList()); } 

你错过了一个抽象。

您的ProviderFactory应该实现IProviderFactory抽象。 这样,您可以将该接口放在应用程序的基础库中,并且可以将ProviderFactory实现放在Composition Root中 。 对于存在于组合根目录中的代码,可以引用DI库,在这种情况下, 您不使用服务位置 。

我最近通过使用DI框架在我自己的代码中解决了一个非常类似的问题。 为了满足依赖性倒置,工厂构造函数应该接受一个接口(正如其他答案所说的那样),但是为了让框架注入正确的类型是棘手的,没有大量的参数列表详细说明每个可能的具体结果。

SimpleInjector允许您注册给定抽象的所有具体结构:

 Container.RegisterCollection(typeof(IProvider), new [] {typeof(TKnown).Assembly,...}); 

您的XML可以列出定义了具体结构的(可能是外部的)程序集,您可以从那里构建程序集数组。 然后你的工厂只需要接受它们并选择一个,可能基于你提到的searchType。

 public class ProviderFactory { private List providers; public ProviderFactory(IEnumerable providers) { this.providers = providers.ToList(); } public IProvider GetProvider(string searchType) { // using a switch here would open the factory to modification // which would break OCP var provider = providers.SingleOrDefault(concretion => concretion.GetType().Name == searchType); if (provider == null) throw new Exception("No provider found of that type. Are you missing an assembly in the RegisterCollection for IProvider?"); return provider; } 

我知道我在这方面迟到了,但假设其他人不认为这种方法有问题,它可能会有用。