这是服务定位器反模式和一个糟糕的解决方案



我已经实现了一个解决方案,该解决方案具有一些核心可重用类,这些类可以使用StructureMap轻松注册和解析。然后,我有一个抽象工厂来在运行时加载其他系列的产品。

如果我有像这样的StructureMap注册表:

    public ProductAClaimsRegistry()
    {           
        var name = InstanceKeys.ProductA;
        this.For<IClaimsDataAccess>().LifecycleIs(new UniquePerRequestLifecycle()).Use<ProductAClaimsDataAccess>().Named(name)
            .Ctor<Func<DbConnection>>().Is(() => new SqlConnection(ConfigReader.ClaimsTrackingConnectionString));
        this.For<IClaimPreparer>().LifecycleIs(new UniquePerRequestLifecycle()).Use<ProductAClaimPreparer>().Named(name);
        this.For<IHistoricalClaimsReader>().LifecycleIs(new UniquePerRequestLifecycle()).Use<ProductAHistoricalClaimReader>().Named(name);
        this.For<IProviderClaimReader>().LifecycleIs(new UniquePerRequestLifecycle()).Use<ProductAProviderClaimReader>().Named(name);    
    }

可能存在ProductBProductC等的版本

然后,我的抽象工厂加载正确命名的实例,如下所示:

public abstract class AbstractClaimsFactory 
{              
    private IClaimsReader claimsReader;
    private IClaimPreparer claimPreparer;
    protected string InstanceKey { get; set; }
    public virtual IClaimsReader CreateClaimReader()
    {
        return this.claimsReader;
    }
    public virtual IClaimPreparer CreateClaimPreparer()
    {
        return this.claimPreparer;     
    }
    public void SetInstances()
    {
        this.CreateInstances();
        var historicalReader = ObjectFactory.Container.GetInstance<IHistoricalClaimsReader>(this.InstanceKey);
        var providerReader = ObjectFactory.Container.GetInstance<IProviderClaimReader>(this.InstanceKey);
        this.claimsReader = new ClaimsReader(historicalReader, providerReader);
        this.claimPreparer = ObjectFactory.Container.GetInstance<IClaimPreparer>(this.InstanceKey);
    }
    protected abstract void CreateInstances();
}

在运行时,有一个处理器类注入了一个具体的工厂,如下所示:

   public void Process(AbstractClaimsFactory claimsFactory)
   { 
       // core algorithm implemented                        
   }

每个产品都有一个混凝土工厂:

public class ProductAClaimsFactory : AbstractClaimsFactory
{       
    public ProductAClaimsFactory()
    {
        SetInstances();
    }
    protected override void CreateInstances()
    {
        InstanceKey = InstanceKeys.ProductA;
    }                   
}

编辑

工厂中加载的类由其他与产品无关的类使用,但它们需要注入ProductAProductB行为。

    public ClaimsReader(IHistoricalClaimsReader historicalClaimsReader, IProviderClaimReader providerClaimsReader)
    {
        this.historicalClaimsReader = historicalClaimsReader;
        this.providerClaimsReader = providerClaimsReader;          
    }

我不太确定这是否是text book abstract factory pattern,我对StructureMap和更先进的DI是新手。

有了这个解决方案,我想我已经强制执行了一个核心算法,并在适当的时候重用了代码。

我还认为它是可扩展的,因为ProductN可以在不更改现有代码的情况下轻松添加。

该解决方案还具有非常好的代码覆盖率,并且测试非常简单。

所以,底线是:我对这个解决方案很满意,但一位同事对它提出了质疑,特别是在使用ObjectFactory.Container.GetInstance<IClaimPreparer>(this.InstanceKey);加载命名实例时,他说它看起来像Service Locator anti pattern

他说得对吗?

如果是的话,有人能指出这个解决方案的缺点吗?我该如何改进它?

服务位置。这是一个问题,因为您引入了对服务定位器ObjectFactory的依赖,而不是AbstractClaimsFactory类实际需要的接口IClaimPreparer。这将使测试更加困难,因为您将很难伪造IClaimPreparer的实现。它还模糊了类的意图,因为类的依赖关系是"不透明的"。

您需要研究组合根的使用来解决反模式。请查看Mark Seemann的作品,了解更多信息。

他部分正确。给定一个好的DI容器,就可以注册所有组件并解析对象树中的根对象。。。DI容器处理创建根对象的所有依赖项(递归),并为您创建整个对象树。然后您可以扔掉DI容器。这样做的好处是,所有对DI容器的引用都被限制在应用程序的入口点。

然而,您至少领先了一步,因为您没有使用依赖关系在对象的构造函数(或其他地方)中解析依赖关系,而是在工厂中解析了这些依赖关系,并通过构造函数注入将它们传递给需要它们的对象;)(这是我在工作的代码中经常看到的东西,这肯定是一种反模式

以下是关于服务定位器以及它们如何成为反模式的更多信息:http://martinfowler.com/articles/injection.htmlhttp://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/

以下是我所暗示的配置解析发布类型模式的更多信息:http://blog.ploeh.dk/2010/08/30/Dontcallthecontainer;它会打电话给你/http://kozmic.net/2010/06/20/how-i-use-inversion-of-control-containers/

相关内容

最新更新