简体   繁体   English

从服务定位器到依赖注入

[英]Moving from Service Locator to Dependency Injection

I prepared sample application based on which I would like to discuss moving to Dependency Injection instead of Service Locator. 我准备了一些示例应用程序,基于这些应用程序,我想讨论转移到依赖注入而不是服务定位器的问题。 I am pretty new in DI so please be patient with me. 我在DI领域还很新,所以请耐心等待。 Sample app is written with Simple Injector as DI library. 示例应用使用Simple Injector作为DI库编写。 Registrations in Bootstrapper works as expected. Bootstrapper中的注册按预期工作。 I have singleton for IMessageBox interface and new instance ComputationCores each time I need it. 每当我需要IMessageBox接口和新实例ComputationCores时,我都会使用它。

I read some articles about DI and so I know that there should be some Composition root and how it should be all working. 我阅读了一些有关DI的文章,因此我知道应该有一些Composition根目录以及它应该如何工作。 But I found just very basic examples without real-word complexity. 但是我发现只是非常基本的示例,没有实词的复杂性。

Sample code: 样例代码:

public class DependencyResolver
{

    public static Func<Type, object> ResolveMe;

    public static T GetInstance<T>() where T : class
    {
        return (T)ResolveMe(typeof (T));
    }
}

public interface IMessageBox
{
    void ShowMessage(string message);
}

public class StandardMessageBox : IMessageBox
{
    public StandardMessageBox()
    {
        Console.WriteLine("StandardMessageBox constructor called...");
    }

    ~StandardMessageBox()
    {
        Console.WriteLine("StandardMessageBox destructor called...");
    }

    public void ShowMessage(string message)
    {
        Console.WriteLine(message);
    }
}

public interface IComputationCoreAlpha
{
    int RunComputation(int myParam);
}

public class SyncComputationCoreAlpha : IComputationCoreAlpha
{
    public SyncComputationCoreAlpha()
    {
        Console.WriteLine("SyncComputationCoreAlpha constructor called...");
    }

    ~SyncComputationCoreAlpha()
    {
        Console.WriteLine("SyncComputationCoreAlpha destructor called...");
    }

    public int RunComputation(int myParam)
    {
        return myParam * myParam;
    }
}

public class AsyncComputationCoreAlpha : IComputationCoreAlpha
{
    public AsyncComputationCoreAlpha()
    {
        Console.WriteLine("AsyncComputationCoreAlpha constructor called...");
    }

    ~AsyncComputationCoreAlpha()
    {
        Console.WriteLine("AsyncComputationCoreAlpha destructor called...");
    }


    public int RunComputation(int myParam)
    {
        return myParam * myParam;
    }
}

public interface IComputationCoreBeta
{
    int RunComputation(int myParam);
}

public class SyncComputationCoreBeta : IComputationCoreBeta
{
    public SyncComputationCoreBeta()
    {
        Console.WriteLine("SyncComputationCoreBeta constructor called...");
    }

    ~SyncComputationCoreBeta()
    {
        Console.WriteLine("SyncComputationCoreBeta destructor called...");
    }


    public int RunComputation(int myParam)
    {
        return myParam * myParam;
    }
}

public class AsyncComputationCoreBeta : IComputationCoreBeta
{
    public AsyncComputationCoreBeta()
    {
        Console.WriteLine("AsyncComputationCoreBeta constructor called...");
    }

    ~AsyncComputationCoreBeta()
    {
        Console.WriteLine("AsyncComputationCoreBeta destructor called...");
    }

    public int RunComputation(int myParam)
    {
        return myParam * myParam;
    }
}

public interface IProjectSubPart
{
    int DoCalculations(int myParam);
}

public class ProjectSubPart1 : IProjectSubPart
{
    public int DoCalculations(int myParam)
    {
        var messageBoxService = DependencyResolver.GetInstance<IMessageBox>();
        messageBoxService.ShowMessage("Hardly working 1...");
        var ccA = DependencyResolver.GetInstance<IComputationCoreAlpha>();
        var ccB = DependencyResolver.GetInstance<IComputationCoreAlpha>();

        return ccA.RunComputation(myParam) + ccB.RunComputation(myParam + 1);
    }
}

public class ProjectSubPart2 : IProjectSubPart
{
    public int DoCalculations(int myParam)
    {
        var messageBoxService = DependencyResolver.GetInstance<IMessageBox>();
        messageBoxService.ShowMessage("Hardly working 2...");
        var ccA = DependencyResolver.GetInstance<IComputationCoreAlpha>();

        return ccA.RunComputation(myParam * 3);
    }
}

public class ProjectSubPartN : IProjectSubPart
{
    public int DoCalculations(int myParam)
    {
        var messageBoxService = DependencyResolver.GetInstance<IMessageBox>();
        messageBoxService.ShowMessage("Hardly working N...");
        return -3;
    }
}

public class ManhattanProject
{
    public void RunProject()
    {
        var messageBoxService = DependencyResolver.GetInstance<IMessageBox>();
        messageBoxService.ShowMessage("Project started...");
        var subPart1 = new ProjectSubPart1();
        var subPart2 = new ProjectSubPart2();
        var subPartN = new ProjectSubPartN();

        var result = subPart1.DoCalculations(1) + subPart2.DoCalculations(2) + subPartN.DoCalculations(3);

        messageBoxService.ShowMessage(string.Format("Project finished with magic result {0}", result));
    }
}

public class Sample
{
    public void Run()
    {
        BootStrapper();

        var mp = DependencyResolver.GetInstance<ManhattanProject>();
        mp.RunProject();

    }

    private void BootStrapper()
    {
        var container = new Container();
        container.RegisterSingle<IMessageBox, StandardMessageBox>();
        container.Register<IComputationCoreAlpha, SyncComputationCoreAlpha>();
        container.Register<IComputationCoreBeta, AsyncComputationCoreBeta>();

        DependencyResolver.ResolveMe = container.GetInstance;
    }

}

In DI it is allowed to call Container.GetInstance (resolve method) just in Composition root, nowhere else. 在DI中,只允许在“合成”根目录中调用Container.GetInstance(resolve方法),而无处可去。 Majority of dependencies should be injected in constructors. 大多数依赖项应注入构造函数中。

Q1: If I would move to DI I suppose that constructor of ManhattanProject should looks something like this: ManhattanProject(IMessageBox mb, IComputationCoreAlpha cca, IComputationCoreBeta ccb). 问题1:如果我要改用DI,我认为ManhattanProject的构造函数应如下所示:ManhattanProject(IMessageBox mb,IComputationCoreAlpha cca,IComputationCoreBeta ccb)。 But this would lead to having one instance per mb, cca, ccb. 但这将导致每个mb,cca和ccb有一个实例。 And not everytime new instance of cca, ccb on my demands. 并非每次都有新的cca实例,ccb都可以满足我的要求。

Q1a: I suppose this could be solved by some kind of abstract factory for cca, ccb which could give me new instance per request. Q1a:我想这可以通过某种抽象工厂来解决,例如cca,ccb,它可以为每个请求提供新实例。 But then - what is the purpose of BootStrapper? 但是,那么-BootStrapper的目的是什么?

Q2: ManhattanProject can consist from more ProjectSubParts which use different coputationCores - eg 42. Thus it is totaly inappropriate to use constructor injection in this way (to provide Computation cores) and some kind of facade should be used. 问题2:ManhattanProject可以由更多使用不同coputationCore(例如42)的ProjectSubParts组成。因此,以这种方式使用构造函数注入(以提供Computation内核)是完全不合适的,应该使用某种外观。 Since facade should have limited count of args in constructor too I would end up with many and many nested facades. 由于Facade在构造函数中的args数量也应该有限,所以我最终会遇到许多嵌套的Facade。 I suppose this is wrong. 我想这是错误的。

Q3: I am using ProjectSubParts which allows me to do some work. Q3:我正在使用ProjectSubParts,它允许我做一些工作。 All inherits from IProjectSubPart interace. 所有继承自IProjectSubPart接口。 In case I would want to inject different implementations for different ProjectSubParts how should I do that? 如果我想为不同的ProjectSubParts注入不同的实现,我该怎么做? Should I create new interface for each ProjectSubpart to allow DI container to resolve which implementation use? 是否应该为每个ProjectSubpart创建新接口,以允许DI容器解析使用哪种实现?

Q4: Based on provided sample (and Service Locator pattern) it would be very easy for me to create instance of IComputationCoreAlpha which could internally create new and clean inner object by calling DependencyResolver.GetInstance everytime I need. Q4:基于提供的示例(和服务定位器模式),我很容易创建IComputationCoreAlpha实例,该实例可以在每次需要时通过调用DependencyResolver.GetInstance在内部创建新的且干净的内部对象。 Moreover I would have full control over them and I could call Dispose on them when I would be done with their usage. 而且,我将完全控制它们,在使用完它们后,可以致电Dispose。 If in DI concept whole graph would be created in CompositionRoot how would be this kind of usage possible? 如果在DI概念中将在CompositionRoot中创建整个图形,那么这种用法怎么可能?

Thanks 谢谢

Q1: If I would move to DI I suppose that constructor of ManhattanProject should looks something like this: ManhattanProject(IMessageBox mb, IComputationCoreAlpha cca, IComputationCoreBeta ccb). 问题1:如果我要改用DI,我认为ManhattanProject的构造函数应如下所示:ManhattanProject(IMessageBox mb,IComputationCoreAlpha cca,IComputationCoreBeta ccb)。

Classes should only depend on services that they need directly. 类应仅取决于直接需要的服务。 So the ManhattanProject should not depend on any computation core, but simply on the IProjectSubPart abstraction. 因此, ManhattanProject不应依赖任何计算核心,而应仅依赖IProjectSubPart抽象。

Q1a: I suppose this could be solved by some kind of abstract factory for cca, ccb which could give me new instance per request. Q1a:我想这可以通过某种抽象工厂来解决,例如cca,ccb,它可以为每个请求提供新实例。 But then - what is the purpose of BootStrapper? 但是,那么-BootStrapper的目的是什么?

The purpose of the bootstrapper / composition root is to build up the object graphs. 引导程序/组合根的目的是建立对象图。 If you create a factory abstraction, it needs to be implemented somewhere. 如果创建工厂抽象,则需要在某个地方实现它。 This 'somewhere' is your composition root. 这个“某处”是您合成的根源。 The factory implementation should be INSIDE your composition root. 工厂实现应在您的合成根目录内部。

Besides using a factory, a better approach is to inject an IEnumerable<IProjectSubPart> . 除了使用工厂之外,更好的方法是注入IEnumerable<IProjectSubPart> Your ManhattanProject will in that case look as follows: 在这种情况下,您的ManhattanProject将如下所示:

public class ManhattanProject
{
    private readonly IMessageBox messageBoxService;
    private readonly IEnumerable<IProjectSubPart> parts;

    public ManhattanProject(IMessageBox messageBoxService, 
        IEnumerable<IProjectSubPart> parts) {
        this.messageBoxService = messageBoxService;
        this.parts = parts;
    }

    public void RunProject() {
        messageBoxService.ShowMessage("Project started...");

        var calculationResults =
            from pair in parts.Select((part, index) => new { part, value = index + 1 })
            select pair.part.DoCalculations(pair.value);

        var result = calculationResults.Sum();

        messageBoxService.ShowMessage(
            string.Format("Project finished with magic result {0}", result));
    }
}

When you depend upon an IEnumerable<IProjectSubPart> you can prevent the ManhattanProject from changing everytime a new IProjectSubPart implementation is added to the system. 当您依赖IEnumerable<IProjectSubPart> ,可以防止在每次将新的IProjectSubPart实现添加到系统时更改ManhattanProject In Simple Injector you can register this as follows: 您可以在Simple Injector中进行如下注册:

// Simple Injector v3.x
container.RegisterSingleton<IMessageBox, StandardMessageBox>();
container.Register<ManhattanProject>();
container.RegisterCollection<IProjectSubPart>(new[] {
    typeof(ProjectSubPart1),
    typeof(ProjectSubPart2),
    typeof(ProjectSubPartN)
});

// Simple Injector v2.x
container.RegisterSingle<IMessageBox, StandardMessageBox>();
container.Register<ManhattanProject>();
container.RegisterAll<IProjectSubPart>(new[] {
    typeof(ProjectSubPart1),
    typeof(ProjectSubPart2),
    typeof(ProjectSubPartN)
});

In general you would even shield other parts of the application from having to know that there are multiple implementations of a certain abstraction, but hiding this doesn't seem possible in your case, because it is the ManhattanProject that is (currently) responsible of providing a different value to each IProjectSubPart . 通常,您甚至可以屏蔽应用程序的其他部分,而不必知道某个抽象有多种实现,但是在您的情况下,隐藏这种实现似乎是不可能的,因为(当前)由ManhattanProject负责每个IProjectSubPart值都不同。 If it would be possible however, the right solution would be to let the ManhattanProject depend upon IProjectSubPart directly instead of depending upon IEnumerable<IProjectSubPart> and you would let the Composition Root inject an composite implementation that would wrap the IEnumerable<IProjectSubPart> as described here . 如果有可能然而,正确的解决办法是让ManhattanProject取决于IProjectSubPart ,而不是直接取决于中IEnumerable<IProjectSubPart>和你会让组成根注入的复合材料实现,将包裹IEnumerable<IProjectSubPart>描述在这里

The same pattern can be applied to all the IProjectSubPart implementations. 可以将相同的模式应用于所有IProjectSubPart实现。 For instance: 例如:

public class ProjectSubPart1 : IProjectSubPart
{
    private readonly IMessageBox messageBoxService;
    private readonly IEnumerable<IComputationCoreAlpha> computers;

    public ProjectSubPart1(IMessageBox messageBoxService,
        IEnumerable<IComputationCoreAlpha> computers) {
        this.messageBoxService = messageBoxService;
        this.computers = computers;
    }

    public int DoCalculations(int myParam) {
        messageBoxService.ShowMessage("Hardly working 1...");

        var calculationResults =
            from pair in computers.Select((computer, index) => new { computer, index })
            select pair.computer.RunComputation(myParam + pair.index);

        return calculationResults.Sum();
    }
}

These IComputationCoreAlpha implementations can be registered as collection as follows: 这些IComputationCoreAlpha实现可以按如下所示注册为集合:

container.RegisterCollection<IComputationCoreAlpha>(new[] {
    typeof(SyncComputationCoreAlpha),
    typeof(AsyncComputationCoreAlpha)
});

Q2: Since facade should have limited count of args in constructor too I would end up with many and many nested facades. Q2:由于在构造函数中facade的args数量也应该有限,所以我最终会遇到很多嵌套的facade。

It's hard to say anything useful about this. 很难说任何有用的东西。 Probably my given implementations with LINQ queries won't work in your case, but your examples are too broad to be very specific about this. 我给定的LINQ查询实现可能不适用于您的情况,但是您的示例过于广泛,无法对此进行非常具体的说明。 In the end you might want to have a custom abstraction, but I'm not sure yet. 最后,您可能需要自定义抽象,但是我不确定。

Q3: I am using ProjectSubParts which allows me to do some work. Q3:我正在使用ProjectSubParts,它允许我做一些工作。 All inherits from IProjectSubPart interface. 所有继承自IProjectSubPart接口。 In case I would want to inject different implementations for different ProjectSubParts how should I do that? 如果我想为不同的ProjectSubParts注入不同的实现,我该怎么做? Should I create new interface for each ProjectSubpart to allow DI container to resolve which implementation use? 是否应该为每个ProjectSubpart创建新接口,以允许DI容器解析使用哪种实现?

That highly depends on your design. 这在很大程度上取决于您的设计。 We should take a look at the Liskov Substitution Principle for this, which basically says that any subtype of a given abstraction should behave in a way that is compatible with the abstraction. 为此,我们应该看一下Liskov替代原理 ,该原理基本上说给定抽象的任何子类型都应以与抽象兼容的方式运行。 So in your case, if a certain class expects a certain IProjectSubPart implementation, and won't function correctly with a different implementation, that means you are breaking the Liskov Substitution Principle. 因此,在您的情况下,如果某个类期望某个IProjectSubPart实现,而在其他实现中无法正常运行,则意味着您正在违反Liskov替换原理。 This means that those implementations do not behave the same, even though they might have the exact method signatures. 这意味着即使这些实现可能具有确切的方法签名,它们的行为也不相同。 In that case you should split them up in multiple interfaces. 在这种情况下,您应该将它们拆分为多个接口。

If a consumer however still functions correctly and changing implementations is just some convenience, than it's okay to let them have the same abstraction. 但是,如果使用者仍然可以正常运行,并且更改实现只是一些方便,那么可以让他们具有相同的抽象是可以的。 Good example is an ILogger abstraction with a FileLogger and MailLogger implementation. 一个很好的例子是带有FileLoggerMailLogger实现的ILogger抽象。 In some parts of the system, you might decide that it is important to send messages through mail. 在系统的某些部分,您可能认为通过邮件发送消息很重要。 Still for the class that depends on ILogger, it will function the same whether or not the messages are written to file, sent through mail, or not sent at all. 对于依赖ILogger的类而言,无论消息是否写入文件,通过邮件发送还是完全不发送消息,它的功能都相同。

Whether or not you are violating LSK is up to you to find out. 是否要违反LSK取决于您自己确定。

Q4: Based on provided sample (and Service Locator pattern) it would be very easy for me to create instance of IComputationCoreAlpha which could internally create new and clean inner object by calling DependencyResolver.GetInstance everytime I need. Q4:基于提供的示例(和服务定位器模式),我很容易创建IComputationCoreAlpha实例,该实例可以在每次需要时通过调用DependencyResolver.GetInstance在内部创建新的且干净的内部对象。 Moreover I would have full control over them and I could call Dispose on them when I would be done with their usage. 而且,我将完全控制它们,在使用完它们后,可以致电Dispose。 If in DI concept whole graph would be created in CompositionRoot how would be this kind of usage possible? 如果在DI概念中将在CompositionRoot中创建整个图形,那么这种用法怎么可能?

I would say that DI actually makes this job easier. 我要说,DI实际上使这项工作变得容易。 For instance, let's try to implement what you want using service locator: 例如,让我们尝试使用服务定位器实现您想要的:

public class LazyComputationCoreAlphaProxy : IComputationCoreAlpha
{
    public int RunComputation(int myParam) {    
        var heavyWeight = DependencyResolver.GetInstance<IComputationCoreAlpha>();
        return heavyWeight.RunComputation(myParam);
    }
}

This is a proxy class that is able to lazily create the real instance when the RunComputation is called. 这是一个代理类,可以在调用RunComputation时延迟创建实际实例。 But this actually presents us with a problem. 但这实际上给我们带来了一个问题。 This becomes clear if we look at how the consumer will use this: 如果我们看看消费者将如何使用它,这将变得很清楚:

public int DoCalculations(int myParam) {
    var ccA = DependencyResolver.GetInstance<IComputationCoreAlpha>();
    return ccA.RunComputation(myParam);
}

Here the DoCalculations method resolves the IComputationCoreAlpha from the service locator. 在这里, DoCalculations方法从服务定位器解析IComputationCoreAlpha This returns us the LazyComputationCoreAlphaProxy instance (since we registered that in our locator). 这将返回LazyComputationCoreAlphaProxy实例(因为我们已在定位器中注册了该实例)。 After resolving it we will call RunComputation on it. 解决后,我们将对其调用RunComputation But inside the RunComputation we resolve IComputationCoreAlpha again. 但是在RunComputation内部,我们再次解析IComputationCoreAlpha We would like to resolve IComputationCoreAlpha , because otherwise our LazyComputationCoreAlphaProxy needs to depend directly on a different implementation, but this would cause a violation of the Dependency Inversion Principle, and will likely cause us to have many different LazyComputationCoreAlphaProxy s. 我们想解决IComputationCoreAlpha ,因为否则我们的LazyComputationCoreAlphaProxy需要直接依赖于不同的实现,但是这会导致违反Dependency Inversion Principle,并且可能导致我们拥有许多不同的LazyComputationCoreAlphaProxy One for each implementation. 每个实现一个。

But if we try to resolve IComputationCoreAlpha here, the locator will return us the LazyComputationCoreAlphaProxy again, and this will eventually result in a stack overflow exception. 但是,如果我们尝试在此处解析IComputationCoreAlpha ,则定位器将再次向我们返回LazyComputationCoreAlphaProxy ,这最终将导致堆栈溢出异常。

Now let's take a look at how this looks with Dependency Injection: 现在让我们看一下依赖注入的外观:

public class LazyComputationCoreAlphaProxy : IComputationCoreAlpha
{
    private readonly Func<IComputationCoreAlpha> factory;

    public LazyComputationCoreAlphaProxy(Func<IComputationCoreAlpha> factory) {
        this.factory = factory;
    }

    public int RunComputation(int myParam) {    
        var heavyWeight = this.factory.Invoke();
        return heavyWeight.RunComputation(myParam);
    }
}

Here we inject a Func factory into the LazyComputationCoreAlphaProxy s constructor. 在这里,我们将Func工厂注入LazyComputationCoreAlphaProxy的构造函数中。 This allows the proxy to stay oblivious to the actual type it creates, while still allowing the same lazy behavior as before. 这使代理可以忽略其创建的实际类型,同时仍然允许与以前相同的惰性行为。 Now we delegated the responsibility of building up that part of the object graph again to our composition root. 现在,我们将重新构建对象图的该部分的职责委托给了我们的合成根。 We could wire this manually as follows: 我们可以按以下方式手动进行连接:

LazyComputationCoreAlphaProxy(() => new SyncComputationCoreAlpha())

Or we can use the decorator facility of Simple Injector to do this for us: 或者,我们可以使用Simple Injector的装饰器工具为我们完成此操作:

container.RegisterCollection<IComputationCoreAlpha>(new[] {
    typeof(SyncComputationCoreAlpha),
    typeof(AsyncComputationCoreAlpha)
});

container.RegisterDecorator(
    typeof(IComputationCoreAlpha), 
    typeof(LazyComputationCoreAlphaProxy));

With the RegisterDecorator registration, Simple Injector will automatically wrap any IComputationCoreAlpha implementation in a LazyComputationCoreAlphaProxy decorator. 通过RegisterDecorator注册,Simple Injector将自动将所有IComputationCoreAlpha实现包装在LazyComputationCoreAlphaProxy装饰器中。 Out of the box, Simple Injector understands Func factory delegates inside decotators and it will ensure that a factory is injected that created the decorated object. 开箱即用,Simple Injector会了解装饰器内的Func工厂代表,并将确保注入创建了装饰对象的工厂。

But since we're on the subject of decorators now. 但是,由于我们现在正在讨论装饰器。 Dependency Injection with decorators gives us even more possibilities to improve the code. 装饰器的依赖注入为我们提供了更多改进代码的可能性。 For instance, much of the code in the IProjectSubPart look alike. 例如, IProjectSubPart中的许多代码看起来相似。 They all have the same message box logging code: 它们都有相同的消息框日志记录代码:

public class ProjectSubPart1 : IProjectSubPart
{
    private readonly IMessageBox messageBoxService;
    private readonly IEnumerable<IComputationCoreAlpha> computers;

    public ProjectSubPart1(IMessageBox messageBoxService,
        IEnumerable<IComputationCoreAlpha> computers) {
        this.messageBoxService = messageBoxService;
        this.computers = computers;
    }

    public int DoCalculations(int myParam) {
        messageBoxService.ShowMessage("Hardly working 1...");

        // part specific calculation
    }
}

If you have many different IProjectSubPart , this is a lot of duplicate code that not only complicates the actual implementations, but also needs to be maintained. 如果您有许多不同的IProjectSubPart ,那么这是很多重复的代码,它们不仅使实际的实现复杂化,而且还需要维护。 What can move that infrastructural concern (or cross-cutting concern) out of those classes, and implement it just once: in a decorator: 什么可以将基础结构问题(或跨领域问题)移出这些类,并仅将其实现一次:在装饰器中:

public class MessageBoxLoggingProjectSubPart : IProjectSubPart
{
    private readonly IMessageBox messageBoxService;
    private readonly IProjectSubPart decoratee;

    public MessageBoxLoggingProjectSubPart(IMessageBox messageBoxService,
        IProjectSubPart decoratee) {
        this.messageBoxService = messageBoxService;
        this.decoratee = decoratee;
    }

    public int DoCalculations(int myParam) {
        messageBoxService.ShowMessage("Hardly working 1...");

        return this.decoratee.DoCalculations(myParam);
    }
}

With this decorator, you can simplify the parts to the following: 使用此装饰器,您可以将零件简化为以下内容:

public class ProjectSubPart1 : IProjectSubPart
{
    private readonly IEnumerable<IComputationCoreAlpha> computers;

    public ProjectSubPart1(IEnumerable<IComputationCoreAlpha> computers) {
        this.computers = computers;
    }

    public int DoCalculations(int myParam) {
        var calculationResults =
            from pair in computers.Select((computer, index) => new { computer, index })
            select pair.computer.RunComputation(myParam + pair.index);

        return calculationResults.Sum();
    }
}

Notice how the ProjectSubPart1 doesn't need to take a dependency upon IMessageBox anymore. 请注意, ProjectSubPart1如何不再需要依赖IMessageBox This cleans up the implementation (and don't forget about the 42 other implementations you have). 这将清理实现(不要忘记您拥有的其他42个实现)。 Again, if we would create such part manually we would do it as follows: 同样,如果我们将手动创建此类零件,则将执行以下操作:

new MessageBoxLoggingProjectSubPart(new ProjectSubPart1(computers));

With Simple Injector however, this becomes even easier: 但是,使用Simple Injector,这变得更加容易:

container.RegisterCollection<IProjectSubPart>(new[] {
    typeof(ProjectSubPart1),
    typeof(ProjectSubPart2),
    typeof(ProjectSubPartN)
});

container.RegisterDecorator(
    typeof(IProjectSubPart), 
    typeof(MessageBoxLoggingProjectSubPart));   

Now any time you want to change the way that things are logged, you only need to change MessageBoxLoggingProjectSubPart . 现在,只要您想更改记录方式,就只需要更改MessageBoxLoggingProjectSubPart For instance when you want to log after the operation has finished, or in case an exception is thrown. 例如,当您想在操作完成后登录时,或者万一抛出异常。 This prevents you from having to make sweeping changes throughout the application (which as what the Open/closed Principle is all about). 这样可以避免您必须对整个应用程序进行彻底的更改(这就是“ 打开/关闭原则”的全部含义)。

I'm sorry for this long post. 对这么长的帖子我很抱歉。 Here are some injected potatoes: 这是一些注入的土豆:

在此处输入图片说明

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

相关问题 服务定位器和依赖注入 - Service locator and dependency injection 没有服务定位器的多实例的依赖注入 - Dependency injection for multiple instance without service locator 服务定位器比依赖注入更容易使用? - Service Locator easier to use than dependency Injection? 使用哪种模式进行记录? 依赖注入或服务定位器? - Which pattern to use for logging? Dependency Injection or Service Locator? 使用依赖注入(Autofac)并避免服务定位器模式 - Using Dependency Injection (Autofac) and avoiding Service Locator pattern 创建易失性接口实例-依赖注入与服务定位器 - Creating volatile interfaces instances - dependency injection vs service locator 如何将此服务定位器模式转换为真正的依赖注入模式? - How to turn this Service Locator pattern into true Dependency Injection pattern? MVVM +服务+实体框架和依赖注入与服务定位器 - MVVM + Services + Entity Framework and Dependency Injection vs Service Locator 是否可以在应用程序启动时不使用服务定位器来实现依赖注入? - Is it possible to implement dependency injection without using service locator at the start of an application? Seemann的依赖注入,“三个呼叫模式”与服务定位器Anit-Pattern - Seemann's Dependency Injection, “Three Calls Pattern” vs Service Locator Anit-Pattern
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM