The Service Locator has been getting a bad press for being an Anti-Pattern. I have used this pattern in the last version on my core code but I won't be using it in the next ...

So what is the purpose of a Service Locator?

To be able to get services for a component which does not tie it to a particular implementation of those services.

Yes I want that, but here are two strong arguments against using this pattern.

Argument 1 - The Singleton Pattern

The first actually boils down to the Singleton Pattern used to implement a globally available Service Locator. Growling at the Singleton Pattern is well known and agreed, basically;

Global single state, insists all components use the singleton in a common state - this is tight coupling. For example, for tests to have reliable results, they should be able to run independently, with no external state which may be effected by other tests and give different results depending on which order they are run in.

Single Responsibility violation, having responsibility for its own creation and the business logic gives a possible reason for change cascade. You should be able to change a component without changing others.

Parallelism, a single mutable object will require a serial access strategy - which means its not parallel.

So, the Singleton is bad, but does the Service Locator Pattern actually include a singleton as part of its implementation? Perhaps it should be thought of as separate to it. Martin Fowler's paper does say ...

"A way to think of this is that service locator is a registry not a singleton. A singleton provides a simple way of implementing a registry, but that implementation decision is easily changed."

Of course, you don't use your Service Locator as a Singleton, you pass it around. That leads on to the next argument.

Argument 2 - Hidden Dependencies

A Service Locator will hide the dependencies of a component from the client.

If a component is able to "locate/resolve" services for itself, anything calling that component will be unaware of the services that are required for it to work.

Problems manifests themselves as a run-time errors when a particular required service has not been registered with the Service Locator, which makes it difficult to code against. The programmer will have to trawl deeper into the code to find its dependencies or use a trial-and-error approach - boo.

Dependency Injection Instead

Specifically Constructor Dependency Injection.

This offers a solution to the above issues, by declaring the dependencies of a component in its constructor;

You ensure that the services required are available when it is instantiated.

They are obvious to anyone programming against it.

Using an abstraction for the service gives you the loose coupling.

class MyComponent {
MyComponent(IMyService service) { }
}

Here is a nice and testable component, using a Fake or a Mock you can easily test this and swap out different implementations of IMyService should requirements change.