C# Inversion of Control – Alternatives to Service-Locator Anti-Pattern

cinversion-of-controlservice-locator

I'm using Unity as IoC with C#, but I guess the question really isn't really limited to Unity and C#, but IoC in general.

I try to follow the SOLID-principle, which means that I got very few dependencies between two concrete classes. But when I need to create new instances of a model, what's the best way to do it?

I usually use a factory create my instances, but there's a few alternatives and I wonder which is better, and why?

Simple Factory:

public class FooFactory : IFooFactory
{
    public IFoo CreateModel()
    {
        return new Foo(); // references a concrete class.
    }
}

Service-locator-factory

public class FooFactory : IFooFactory
{
    private readonly IUnityContainer _container;

    public FooFactory (IUnityContainer container)
    {
        _container = container;
    }

    public IFoo CreateModel()
    {
        return _container.Resolve<IFoo>(); // Service-locator anti-pattern?
    }
}

Func-factory. No dependencies to other classes.

public class FooFactory : IFooFactory
{
    private readonly Func<IFoo> _createFunc;

    public FooFactory (Func<IFoo> createFunc)
    {
        _createFunc= createFunc;
    }

    public IFoo CreateModel()
    {
        return _createFunc(); // Is this really better than service-locator?
    }
}

Which IFooFactory should I use, and why? Is there a better option?

The examples above are of a more conceptual level, where I try to find a balance between SOLID, maintainable code and service locator. Here's an actual example:

public class ActionScopeFactory : IActionScopeFactory
{
    private readonly Func<Action, IActionScope> _createFunc;

    public ActionScopeFactory(Func<Action, IActionScope> createFunc)
    {
        _createFunc = createFunc;
    }

    public IActionScope CreateScope(Action action)
    {
        return _createFunc(action);
    }
}

public class ActionScope : IActionScope, IDisposable
{
    private readonly Action _action;

    public ActionScope(Action action)
    {
        _action = action;
    }

    public void Dispose()
    {
        _action();
    }
}

public class SomeManager
{
    public void DoStuff()
    {
        using(_actionFactory.CreateScope(() => AllDone())
        {
           // Do stuff. And when done call AllDone().
           // Another way of actually writing try/finally.
        }
    }
}

Why do I use a Factory at all? Because I sometimes need to create new models. There are various scenarios when this is necessary. For eg. in a mapper and when the mapper has a longer lifetime than the object it should map. Example for factory usage:

public class FooManager
{
    private IService _service;
    private IFooFactory _factory;

    public FooManager(IService service, IFooFactory factory)  
    {
        _service = service;
        _factory = factory;
    }

    public void MarkTimestamp()
    {
        IFoo foo = _factory.CreateModel();
        foo.Time = DateTime.Now;
        foo.User = // current user
        _service.DoStuff(foo);
    }

    public void DoStuffInScope()
    {
        using(var foo = _factoru.CreateModel())
        {
            // do stuff with foo...
        }
    }
}

Best Answer

First things first, the mantra that service-locators are an anti-pattern is tiresome and counter productive. They have their downsides, but they're pretty much the same as conventional IoC containers except IoC containers are good.

That said, let's focus on your examples:

Simple factories are good. They are clear. They are easy to test. They are easy to extend.

Service locator factories are overkill for this scenario (and most scenarios, frankly). Service locators let you have a factory that can resolve from any arbitrary type to an instance of that type (or an exception). You don't need that here. You just need to be able to supply some IFoo.

The func-factory is weird here. You don't really need the interface, since Func<IFoo> is the interface (in the "a point where two things interact" definition). Sometimes you already have the interface and the func-factory isn't really a factory, but an adapter between the capital I interface and the func "interface".

Which is best will vary on your needs. Personally, I like just having a Func. It provides the most flexibility in use, while not having the boilerplate overhead of the simple factory.

And as much as I defend service locators as not being an anti-pattern, they do have a very limited use case, and your examples do not even come close to it.