简体   繁体   English

这是服务定位器的反模式,并且是较差的解决方案吗?

[英]Is this the Service Locator Anti Pattern and a poor solution?

I have implemented a solution that has some core reusable classes that are easily registered and resolved using StructureMap. 我实现了一个解决方案,该解决方案包含一些可重用的核心类,可以使用StructureMap轻松注册和解决这些类。 I then have an abstract factory to load additional families of products at runtime. 然后,我有了一个抽象工厂,可以在运行时加载其他系列的产品。

If I have a StructureMap registries like this one: 如果我有一个这样的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);    
    }

There may be a version for ProductB , ProductC and so on. 可能会有ProductBProductC等版本。

My abstract factory then loads the correct named instance like this: 然后,我的抽象工厂将加载正确的命名实例,如下所示:

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

At runtime there is a processor class that has a concrete factory injected like this: 在运行时,有一个处理器类,其中注入了一个具体的工厂,如下所示:

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

A concrete factory then exists for each product: 每个产品都有一个具体的工厂:

public class ProductAClaimsFactory : AbstractClaimsFactory
{       
    public ProductAClaimsFactory()
    {
        SetInstances();
    }

    protected override void CreateInstances()
    {
        InstanceKey = InstanceKeys.ProductA;
    }                   
}

EDIT 编辑

The classes loaded in the factory are used by other classes that are Product agnostic - but they need to inject ProductA or ProductB behaviour. 在工厂中加载的类由与产品无关的其他类使用-但它们需要注入ProductAProductB行为。

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

I'm not exactly sure if this a text book abstract factory pattern and I'm new to StructureMap and more advance DI in general. 我不能完全肯定,如果这是一个text book abstract factory pattern ,我是新来的StructureMap和更先进的DI一般。

With this solution I think I have enforced a core algorithm and reused code where appropriate. 通过这种解决方案,我认为我已经在适当的地方实施了核心算法并重用了代码。

I also think that it is extensible as ProductN can easily be added without changing existing code. 我也认为它是可扩展的,因为可以很容易地添加ProductN而无需更改现有代码。

The solution also has very good code coverage and the tests are very simple. 该解决方案还具有很好的代码覆盖率,并且测试非常简单。

So, bottom line is: I am fairly happy with this solution but a colleague has questioned it, specificly around using ObjectFactory.Container.GetInstance<IClaimPreparer>(this.InstanceKey); 因此,最重要的是:我对这种解决方案相当满意,但是一位同事对此提出了质疑,特别是围绕使用ObjectFactory.Container.GetInstance<IClaimPreparer>(this.InstanceKey); to load named instances and he said it looks like the Service Locator anti pattern . 加载命名实例,他说这看起来像Service Locator anti pattern

Is he correct? 他说的对吗?

If so, can anyone point out the downsides of this solution and how I might go about improving it? 如果是这样,谁能指出该解决方案的弊端,以及我将如何改进它?

This is service location. 服务位置。 It's a problem as you have introduced a dependency on your service locator, ObjectFactory , rather than the interface, IClaimPreparer , your AbstractClaimsFactory class actually needs. 这是一个问题,因为您已经引入了对服务定位器ObjectFactory的依赖关系,而不是对AbstractClaimsFactory类实际需要的接口IClaimPreparer This is going to make testing harder as you'll struggle to fake an implementation of IClaimPreparer . 这将使测试变得更加困难,因为您将很难伪造IClaimPreparer的实现。 It also clouds the intention of your class as the class's dependencies are 'opaque'. 由于类的依赖关系是“不透明的”,因此这也模糊了类的意图。

You need to look into the use of a Composition Root to resolve the anti-pattern. 您需要研究使用合成根来解决反模式。 Have a look at Mark Seemann's work to find out more. 看看Mark Seemann的工作以了解更多信息。

He's partially correct. 他部分正确。 Given a good DI container it is possible to register all your components and resolve the root object in your object tree... the DI container handles creating all the dependency for the root object (recursively) and creates the whole object tree for you. 给定一个良好的DI容器,可以注册所有组件并在对象树中解析根对象... DI容器处理(递归地)为根对象创建所有依赖关系,并为您创建整个对象树。 Then you can throw the DI container away. 然后,您可以将DI容器扔掉。 The nice thing about doing it that way is all references to DI container are confined to the entry-point of your app. 这样做的好处是,所有对DI容器的引用都限于应用程序的入口点

However, you are at least one step ahead of the curve since you didn't resolve dependencies in the constructor (or somewhere else) of the object using them, but instead resolved those in the factory and passed them in to the objects that need them via constructor-injection ;) (That's something I see often in code I work on and that is definitely an anti-pattern. 但是,您至少比曲线领先一步,因为您没有使用它们解决对象的构造函数(或其他地方)中的依赖关系,而是解决了工厂中的依赖关系并将其传递给需要它们的对象通过builder-injection;)(这是我在工作的代码中经常看到的东西,这绝对是一种反模式。

Here's a bit more about service locators and how they can be an anti-pattern: http://martinfowler.com/articles/injection.html http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/ 以下是有关服务定位器及其如何成为反样式的更多信息: http : //martinfowler.com/articles/injection.html http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/

Here's a bit more about the configure-resolve-release type pattern I hinted at: http://blog.ploeh.dk/2010/08/30/Dontcallthecontainer;itllcallyou/ http://kozmic.net/2010/06/20/how-i-use-inversion-of-control-containers/ 以下是有关我提示的configure-resolve-release类型模式的更多信息: http : //blog.ploeh.dk/2010/08/30/Dontcallthecontainer; itllcallyou/ http://kozmic.net/2010/06/20 /如何-I-使用-反转的控制的容器/

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

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