views:

401

answers:

4

On this AutoFac "Best Practices" page (http://code.google.com/p/autofac/wiki/BestPractices), they say:

Don't Pass the Container Around Giving components access to the container, or storing it in a public static property, or making functions like Resolve() available on a global 'IoC' class defeats the purpose of using dependency injection. Such designs have more in common with the Service Locator pattern. If components have a dependency on the container, look at how they're using the container to retrieve services, and add those services to the component's (dependency injected) constructor arguments instead.

So what would be a better way to have one component "dynamically" instantiate another? Their second paragraph doesn't cover the case where the component that "may" need to be created will depend on the state of the system. Or when component A needs to create X number of component B.

A: 

Service Locator patterns are more difficult to test and it certainly is more difficult to control dependencies, which may lead to more coupling in your system than you really want.

If you really want something like lazy instantiation you may still opt for the Service Locator style (it doesn't kill you straight away and if you stick to the container's interface it is not too hard to test with some mocking framework). Bear in mind, though that the instantiation of a class that doesn't do much (or anything) in the constructor is immensely cheap.

The container's I have come to know (not autofac so far) will let you modify what dependencies should be injected into which class depending on the state of the system such that even those decisions can be externalized into the configuration of the container, which can provide you plenty of flexibility without resorting to implementing interaction with the container based on some state you consider in the dependency comsuming class.

flq
A: 

An IoC takes the responsibility for determining which version of a dependency a given object should use. This is useful for doing things like creating chains of objects that implement an interface as well as having a dependency on that interface (similar to a chain of command or decorator pattern).

By passing your container, you are putting the onus on the individual object to get the appropriate dependency, so it has to know how to. With typical IoC usage, the object only needs to declare that it has a dependency, not think about selecting between multiple available implementations of that dependency.

kyoryu
+8  A: 

To abstract away the instantiation of another component, you can use the Factory pattern:

public interface IComponentBFactory
{
    IComponentB CreateComponentB();
}

public class ComponentA : IComponentA
{
    private IComponentBFactory _componentBFactory;

    public ComponentA(IComponentBFactory componentBFactory)
    {
        _componentBFactory = componentBFactory;
    }

    public void Foo()
    {
        var componentB = _componentBFactory.CreateComponentB();

        ...
    }
}

Then the implementation can be registered with the IoC container.

A container is one way of assembling an object graph, but it certainly isn't the only way. It is an implementation detail. Keeping the objects free of this knowledge decouples them from infrastructure concerns. It also keeps them from having to know which version of a dependency to resolve.

Bryan Watts
I was going to suggest the same. This is the way to go here in my opinion.
Jani Hartikainen
Good answer Bryan. I actually knew the answer... but asking a question seemed like the only way I could start getting points on the StackOverflow system. Great explanation!
Wil Bloodworth
+3  A: 

Autofac actually has some special functionality for exactly this scenario - the details are on the wiki here: http://code.google.com/p/autofac/wiki/DelegateFactories.

In essence, if A needs to create multiple instances of B, A can take a dependency on Func<B> and Autofac will generate an implementation that returns new Bs out of the container.

The other suggestions above are of course valid - Autofac's approach has a couple of differences:

  • It avoids the need for a large number of factory interfaces
  • B (the product of the factory) can still have dependencies injected by the container

Hope this helps!

Nick

Nicholas Blumhardt