views:

517

answers:

1

Hi everyone:

Just assume I have some class Foo, that has two dependencies: an ISerializer and an IFileAccessHandler.

Now this class also has other depedencies, functional dependencies. I don't want anyone instantiating this class in an invalid state, so I'd also need to pass a domain object in the constructor.

But how can I have that handled by IoC when I also know what domain object to pass in the moment I'm actually creating class Foo?

I made the domain object a property that I have set by a Factory. So the Factory makes a Service Locator call to get a properly instantiated "Foo" class with it's dependencies, and further fills it up with the correct domain object and returns it.

But is this the best way to go? I would have prefered having the domain object part of my constructor to make it apparant you actually need to work with "Foo".

Any ideas? Am I missing something here? Thx in advance!

+10  A: 

The default solution to DI when you can't wire up a concrete type at registration time is to use an Abstract Factory

In your case, I would define an IFooFactory interface:

public interface IFooFactory
{
    Foo Create(DomainClass dc);
}

This will allow you to define a concrete implementation that knows about your infrastructure services.

public class FooFactory : IFooFactory
{
    private readonly ISerializer serializer;
    private readonly IFileAccessHandler fileHandler;

    public FooFactory(ISerializer serializer, IFileAccessHandler fileHandler)
    {
        if(serializer == null)
        {
            throw new ArgumentNullException("serializer");
        }
        if(fileHandler == null)
        {
            throw new ArgumentNullException("fileHandler");
        }

        this.serializer = serializer;
        this.fileHandler = fileHandler;
    }

    public Foo Create(DomainClass dc)
    {
        return new Foo(this.serializer, this.fileHandler, dc);
    }
}

In this way you can protect the invariants of your Foo class, enabling you to stay with Constructor Injection.

In the DI container, you can register the IFooFactory and corresponding implementation. Everywhere you have a DomainClass instance and need a Foo instance, you would then take a dependency on IFooFactory and use that.

Mark Seemann
Cool! This is the answer I was looking for! Thx guys!
Laila
Why do you create Foo manually? In that case FooFactory has unnecessary dependencies. Why don't you use container.Resolve<Foo>(dc) instead of new Foo(this.serializer, this.fileHandler, dc)?
Yauheni Sivukha
Because that would be the Service Locator anti-pattern (http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx). The DI Container must not invade the rest of the application. However, some containers (e.g. Windsor) can automatically implement and emit implementations of Abstract Factories, in which case FooFactory would be completely redundant. If you want to use the DI Container, go all the way :)
Mark Seemann
I agree that Service Locator is anti-pattern, but every rule has exceptions. You should not follow this rule blindly! You wire your objects manually, your abstract factory has incorrect dependencies. This a smell of bad design, is not it? DI container is intended to get rid of this smells, is not it? So use DI container to create Foo! You right :) If you want to use the DI Container, go all the way :)
Yauheni Sivukha
Dynamic factories á la Windsor aside, why would FooFactory have incorrect dependencies? It still uses Constructor Injection, so you still need the DI Container to wire it up for you. The point is that you can't create Foo without DomainClass, which is only available at run-time. Using the DI Container as a Service Locator is not going to change that, but would add a tight coupling to the DI Container, as well as a logical coupling to Foo. This would only leave you worse off than the above implementation.
Mark Seemann