【问题标题】:Factory pattern without service locator没有服务定位器的工厂模式
【发布时间】:2013-09-26 08:31:49
【问题描述】:

我目前正试图编写一个不依赖于服务位置的工厂类。

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

提供程序本身是完全复杂的类,具有自己的依赖项,因此无法手动构建。

更新的服务位置示例:

    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。

【问题讨论】:

  • 为什么首先需要这么多依赖对象?
  • 根据搜索类型,我需要调用一组不同的提供程序。一般来说,这也是关于工厂模式的一个有效问题,因为它的工作是创建违反 IoC 原则的具体实例。
  • 您使用的是什么 DI 框架? Ninject 有一个工厂扩展,非常适合这个。
  • 补充我的评论,简而言之,工厂类将包含为给定搜索类型执行哪些提供程序的业务逻辑。执行提供者和聚合结果的任务留给工厂的消费者。工厂本身将被构造函数注入。
  • @SimonWhitehead 不幸的是,我正在使用 Unity。我已更新问题以反映这一点。

标签: c# inversion-of-control factory service-locator


【解决方案1】:

另一种方法是将Func&lt;Type, object&gt; 传递给构造函数并通过您的容器实现该功能:

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

【讨论】:

  • 谢谢@Bojan,但这看起来只是掩盖传递真正落入服务位置灰色区域的 DI 容器,特别是因为 Func 不清楚什么它的意思是。
  • 我同意目前尚不清楚它立即意味着什么 - 但也很容易声明一个具有更多描述性名称的委托(例如public object delegate CreateProvider(Type providerType))并使用它而不是Func。但是,这里没有服务地点,正如通常所理解的那样。任何方法都必须在底层使用 DI 容器,我认为这样做不一定是坏事,但是使用委托可以更方便地进行测试。
  • 我认为委托方式中奖了。它将类型的解析与其余代码封装在一起,并且委托被命名为属性,对于额外的点,我已强烈键入委托以返回 IProvider,因此您将无法轻易滥用它。
【解决方案2】:

你缺少一个抽象。

您的ProviderFactory 应该实现IProviderFactory 抽象。这样,您可以将该接口放置在应用程序的基础库中,并且可以将 ProviderFactory 实现放置在您的 Composition Root 中。对于位于组合根目录中的代码,可以引用 DI 库,在这种情况下是 you're not using service location

【讨论】:

  • 那么您是主张具体的 ProviderFactory 存在于 Global.asax 中(这是一个 WebAPI 项目)并直接引用 DI 容器,还是我读错了? ProviderFactory 包含业务逻辑,我不确定这是否合适。
  • 是的,我主张所有引用容器的类都存在于组合根中。我不提倡的是你将业务逻辑放在你的组合根中。确保业务逻辑放置在您的应用程序中并且不引用容器。你应该相应地改变你的设计。
  • 有一个简单的例子可以展示吗?我无法描绘不涉及在引导阶段使用我的 DI 容器正常注册 IProviderFactory 或通过构造函数注入将 DI 容器传递给 ProviderFactory 的解决方案。
  • 您能否展示一个更完整的ProviderFactory 实现,其中包含更多业务逻辑?如果你这样做了,我也许可以重构它并向你展示一个替代实现。
  • 我已经用一个更完整的例子更新了这个问题,但请注意我还没有真正构建这个类。我很好奇如何将 ProviderFactory 集成到组合根中,因为这将解决我的 Udi Dahan 域事件的非静态版本,其中涉及将服务定位器引导到静态属性中。
【解决方案3】:

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

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-07
    • 2018-12-13
    • 1970-01-01
    • 2019-12-26
    • 1970-01-01
    • 1970-01-01
    • 2019-06-29
    • 1970-01-01
    相关资源
    最近更新 更多