Dependency Injection - new instance required in se

2019-01-06 12:38发布

问题:

I have some code that looks something like this:

public MyService(IDependency dependency)
{
    _dependency = dependency;
}

public Message Method1()
{
    _dependency.DoSomething();

}

public Message Method2()
{
    _dependency.DoSomething();  
}

public Message Method2()
{
    _dependency.DoSomething();  
}

Now I have just realised that because the dependency object contains internal state information. I need to new up a new instance of it in each method call

So what is the best way to do this and still not have a concrete instance newed up ?

Would you use an IoC container and make a call to to the container in each and every one of the methods? Or is there a slicker way where you can only make one call to the container?

What if I wasn't using an IoC container - would there be a way to not new up a concrete instance in each method?

回答1:

Most of the answers here so far suggest that you change the injected dependency type to some sort of Abstract Factory (a Func<T> is also an Abstract Factory) to address the issue. However, if you do that it would be a leaky abstraction because you would let the knowledge of a particular implementation determine the design of the consumer. This violates the Liskov Substitution Principle.

A better option is to keep MyService as it is, and then create a wrapper for IDependency that addresses the particular lifetime issue:

public class TransientDependencyWrapper : IDependency
{
    public void DoSomething()
    {
        new MyStatefulDependency().DoSomething();
    }
}

Inject that into MyService instead of directly injecting the original implementation (MyStatefulDependency).

If you want to abstract away the creation of the dependency, you can always inject an Abstract Factory at this level.



回答2:

It sounds like you ought to inject a provider/factory. How you represent that is up to you (and what your IoC supports) - it could be as simple as:

public MyService(Func<IDependency> dependencyProvider)
{
    this.dependencyProvider = dependencyProvider;
}

public Message Method1()
{
    IDependency dependency = dependencyProvider();
    dependency.DoSomething();    
}

... or you could have an interface for this specific factory type, or a generic IFactory<T> etc. There are all kinds of possibilities - but the core bit is that you inject what you need, which in this case is "a way of creating a new implementation of IDependency on each call".



回答3:

If you need to create multiple instances of an injected type, you should inject an IDependencyFactory instead which would be responsible for controlling instance lifecycles:

interface IDependencyFactory
{
    IDependency GetInstance();
}


回答4:

You could do it like this:

private readonly Func<IDependancy> _dependancy;
public MyService(Func<IDependancy> dependancy)
{
    _dependancy = dependancy;
}

public Message Method1()
{
    _dependancy().DoSomething();
}

public Message Method2()
{
    _dependancy().DoSomething();  
}

public Message Method3()
{
    _dependancy().DoSomething();  
}

And then:

var service = new MyService(() => new SomeConcreteDependency());
service.Method1();
service.Method2();
service.Method3();


回答5:

First two ideas that come to my head are

  1. Don't take it in on the constructor, but take it in on each method.
  2. Instead of passing an instance into the constructor, pass in a factory or a Func<IDependency> that will create a new instance each time you call the func.