我目前正试图编写一个不依赖服务位置的工厂类。
我能想到的唯一其他选择是使用构造函数注入来注入所有可能的实例,但这可能会导致意外,因为类是通过引用传递的。 一旦可能的提供商数量增加,它也可能会变得昂贵和混乱。
提供程序本身是完全复杂的类,它们有自己的依赖关系,因此手动构造是不可能的。
更新了服务位置示例:
public class ProviderFactory : IProviderFactory
{
private readonly IProviderConfigurationService _providerConfigurationService;
public enum SearchType
{
Foo,
Bar
}
public ProviderFactory(IProviderConfigurationService providerConfigurationService)
{
_providerConfigurationService = providerConfigurationService;
}
public Collection<IProvider> 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<IProvider>(providerList.ForEach(x=> ServiceLocator.GetInstance(typeof(x))).ToList()) ;
}
}
我的其他选择是什么?我目前正在使用Unity进行DI。
答案 0 :(得分:2)
另一种方法是将Func<Type, object>
传递给构造函数并通过容器实现该函数:
unity.RegisterInstance<Func<Type, object>>(t => unity.Resolve(t))
然后在你的课堂上:
public ProviderFactory(Func<Type, object> createFunc, IProviderConfigurationService pcs)
{
_createFunc = createFunc;
}
public Collection<IProvider> GetProviderInstances(SearchType searchType)
{
var providerList = _providerConfigurationService.GetProviderList(searchType);
return new Collection<IProvider>(providerList.Select(_createFunc).ToList());
}
答案 1 :(得分:1)
你错过了抽象。
您的ProviderFactory
应该实现IProviderFactory
抽象。这样您就可以将该界面放在应用程序的基础库中,并将ProviderFactory
实现放在Composition Root中。对于存在于组合根目录中的代码,可以引用DI库,在这种情况下you're not using service location。
答案 2 :(得分:0)
我最近通过使用DI框架在我自己的代码中解决了一个非常类似的问题。为了满足依赖倒置,工厂构造函数应该接受一个接口(正如其他答案所说的那样),但是为了让框架注入正确的类型是棘手的,没有大量的参数列表详细说明每个可能的具体结果。
SimpleInjector允许您使用以下命令注册给定抽象的所有具体结构:
Container.RegisterCollection(typeof(IProvider), new [] {typeof(TKnown).Assembly,...});
您的XML可以列出定义了具体结构的(可能是外部的)程序集,您可以从那里构建程序集数组。然后你的工厂只需要接受它们并选择一个,可能基于你提到的searchType。
public class ProviderFactory
{
private List<IProvider> providers;
public ProviderFactory(IEnumerable<IProvider> 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;
}
我知道我在这方面迟到了方式,但假设其他人不认为这种方法有问题,那可能会有用。