Service Locator: roles vs. mechanics by Mark Seemann
It's time to take a step back from the whole debate about whether or not Service Locator is, or isn't, an anti-pattern. It remains my strong belief that it's an anti-pattern, while others disagree. Although everyone is welcome to think differently than me, I've noticed that some of the arguments being put forth in defense of Service Locator seem very convincing. However, I believe that in those cases we no longer talk about Service Locator, but something that looks an awful lot like it.
Some APIs are easy to confuse with a ‘real' Service Locator. It probably doesn't help that last year I published an article on how to tell the difference between a Service Locator and an Abstract Factory. In this article I may have focused too much on the mechanics of Service Locator, but as Derick Bailey was so kind to point out, this hides the role the API might play.
To repeat that earlier post, a Service Locator looks like this:
public interface IServiceLocator { T Create<T>(object context); }
All Service Locators I've seen so far look like that, or some variation thereof, but that doesn't mean that the relationship is transitive. Just because an API looks like that it doesn't automatically means that it's a Service Locator.
If it was, all DI containers would be Service Locators. As an example, here's Castle Windsor's Resolve method:
public T Resolve<T>()
Even AutoFixture has an API like that:
MyClass sut = fixture.CreateAnonymous<MyClass>();
It has never been my intention to denounce every single DI container available, as well as my own open source framework. Service Locator is ultimately not identified by the mechanics of its API, but by the role it plays.
A DI container encapsulated in a Composition Root is not a Service Locator - it's an infrastructure component.
It becomes a Service Locator if used incorrectly: when application code (as opposed to infrastructure code) actively queries a service in order to be provided with required dependencies, then it has become a Service Locator.
Service Locators are spread thinly and pervasively throughout a code base - that is just as much a defining characteristic.
Comments
The anti-pattern comes from a DI container used as a ServiceLocator?
A DI Container is not, in itself, a Service Locator, but it can be used like one. If you do that, it's an anti-pattern, but that doesn't mean that any use of a container constitutes an anti-pattern. When a container is used according to the Register Resolve Release pattern from within a Composition Root it's all good.
I pop by your blog intermittently: it's good stuff.
For some reason, I always had a particular idea of the type of person you are; but for some other reason, I thought that this stemmed from something other than your (excellent) writings.
I've just realised what it is.
That photo of you, up there on the top right, your, "Contact," photo.
It looks like you have an ear-ring dangling from your left ear.
I have one concern about Composition Root. For WPF applications, you said that the Composition Root is at OnStartUp. So, if I want to compose the main window and all other windows (with their view models) in the app at once and one place only (I mean at OnStartUp). How could I do? Thanks in advance!
namespace SimpleCSharpApp
{
class Program
{
static void Main(string[] args)
{
// resolve object using Unity
IUnityContainer container = new UnityContainer();
foreach (var t in typeof(Program).Assembly.GetExportedTypes())
{
if (typeof(IMessageWriter).IsAssignableFrom(t))
{
container.RegisterType(typeof(IMessageWriter), t, t.FullName);
}
}
container.Resolve<Salutation>().Exclaim();
Console.ReadLine();
}
}
public class Salutation
{
private readonly IMessageWriter writer;
public Salutation(IMessageWriter writer)
{
this.writer = writer;
}
public void Exclaim()
{
writer.Write("Hello DI!");
}
}
public interface IMessageWriter
{
void Write(string message);
}
public class ConsoleMessageWriter : IMessageWriter
{
public void Write(string message)
{
Console.WriteLine(message);
}
}
}