简体   繁体   English

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

[英]Factory pattern without service locator

I am currently stuck at trying to write a factory class that doesn't rely on service location. 我目前只能尝试编写不依赖服务位置的工厂类。

The only other alternative I can think of is to use constructor injection to inject all possible instances, but that may lead to surprises as classes are passed via reference. 我能想到的唯一其他选择是使用构造函数注入来注入所有可能的实例,但是当类通过引用传递时,这可能会导致意外。 It is also possibly going to be costly and messy once the number of possible providers grow. 一旦可能的提供商数量增加,它也可能会变得昂贵且混乱。

The providers themselves are full complex classes that have their own dependencies so manual construction is out of the picture. 提供程序本身是完全复杂的类,它们具有自己的依赖关系,因此手动构造无法实现。

Updated service location example: 更新的服务位置示例:

    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()) ;
        }
    }

What are my other options? 我还有其他选择吗? I am currently using Unity for DI. 我目前正在使用Unity for DI。

An alternative is to pass a Func<Type, object> to the constructor and to implement the function through your container: 一种替代方法是将Func<Type, object>传递给构造函数,并通过您的容器实现该函数:

unity.RegisterInstance<Func<Type, object>>(t => unity.Resolve(t))

Then in your class: 然后在您的课程中:

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());
}

You are missing an abstraction. 您缺少抽象。

Your ProviderFactory should implement an IProviderFactory abstraction. 您的ProviderFactory应该实现IProviderFactory抽象。 This way you can place that interface in a base library of your application and you can place the ProviderFactory implementation inside your Composition Root . 这样,您可以将该接口放置在应用程序的基础库中,并且可以将ProviderFactory实现放置在您的Composition Root中 For code that lives inside your composition root, it is okay to reference the DI library, and in that case you're not using service location . 对于位于合成根目录中的代码,可以引用DI库,在这种情况下, 您无需使用service location

I have recently solved a very similar issue in my own code by using a DI framework. 最近,我使用DI框架在自己的代码中解决了一个非常相似的问题。 To satisfy Dependency Inversion, the factory constructor should accept an interface (as the other answers have said), but to get the framework to inject the right type is tricky without having a massive list of arguments detailing each possible concretion. 为了满足Dependency Inversion,工厂构造函数应接受一个接口(如其他答案所述),但是要获取注入正确类型的框架却很棘手,而没有大量详细列出每种可能的构想的参数。

SimpleInjector allows you to register all concretions of a given abstraction with: SimpleInjector允许您使用以下命令注册给定抽象的所有具体构想:

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

Your XML could list the (possibly external) assemblies where the concretions are defined and you could build the assembly array from there. 您的XML可以列出定义了具体对象的(可能是外部的)程序集,您可以从那里建立程序集数组。 Then your factory just needs to accept them all and pick one, perhaps based on the searchType you mentioned. 然后,您的工厂只需要接受它们并选择一个即可,也许基于您提到的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;
    }

I know I'm way late to the party on this but assuming other folks don't see this approach as problematic, it might be useful. 我知道我在这方面迟到了,但是假设其他人认为这种方法没有问题,它可能会有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM