An example of a benign Service Locator

In my last post, I examined whether or not the Service Locator is an anti-pattern. The conclusion was that it isn’t. Instead it’s a code smell that all too often causes another anti-pattern to occur: hindering easy testing.

One of the reasons cited for why it’s only a code smell is due to edge cases where a service locator can be used internally within an assembly as an implementation detail that doesn’t affect testing. It’s natural to ask what such a test-friendly service locator might look like. The Visual Studio/MSBuild and .NET framework combination supplies such a locator: .resx files and the ResourceManager class.

If we create a simple Strings.resx file with the following contents:

We can then write simple code to access the resources:

This will then print Very good.

That’s all fine and well, but doesn’t give us a service locator. To achieve that’s let’s add a second resources file,

Now, assuming your locale isn’t “fr-FR”, the following code will print Very good, followed by Très bien:

The ResourceManager associated with Strings is now acting as a service locator, with the CurrentUICulture determining which resource service is used to resolve String2.

This locator isn’t 100% testing-friendly, as CultureInfo.CurrentUICulture has global state and so tests that affect this global state would be brittle. However, it’s questionable as to whether such tests would have any value anyway. Another criticism that could be levelled at this locator is a lack of compile-time discoverability. The Strings class comes to the rescue here. It has a set of helper properties that automatically match the resources defined within the .resx file, so the resources can be accessed in a guaranteed to exist, type-safe fashion. The above code can be rewritten as:

With the previously mentioned caveat of global state, we have a service locator that offers referential transparency (the resources are constants for a specific locale), compile-time discoverability and test-friendliness. It could be replaced with dependency injection, but this would complicate the code and offer no real benefit. Let me restate: this is an edge case. The vast majority of use-cases of service locators lead to anti-patterns. This point cannot be overstated enough and in no way am I trying to defend its use in those cases. The edge case exists though, which is why I feel the Service Locator pattern isn’t, by itself, an automatic anti-pattern.

2 thoughts on “An example of a benign Service Locator

  1. Is the ResourceManager API a Service Locator?

    Isn’t it rather an example of the Ambient Context pattern that I describe in my book? It seems to fit the description fairly well, since resources are bound to an ambient context (in this case, Thread Local Storage) and have somewhat safe fall-back mechanisms.

    A Service Locator tends to have a particular ‘shape’, as I’ve previously attempted to describe:

    Granted, ResourceManager does have a GetObject method that fits the description of an untyped Service Locator, but is it ever used like that?

    If we re-read Martin Fowler’s original article we’re reminded that “a service locator is […] an object that knows how to get hold of all of the services that an application might need.” Is that how we use the ResourceManager API?

  2. Mark,

    Thanks for taking the time to comment.

    You make some interesting points, which highlight the problem with patterns, namely their definitions. To my mind, a value, object etc is either obtained via “ask” (service locator) or “tell” (injection). So I use a very broad definition of what a service locator is. Martin Fowler’s definition might appear more precise, but an IoC container is “an object that knows how to get hold of all of the services that an application might need”, which is dutifully injects into that application. Are IoC containers therefore service locators (not just when implemented badly, but always?) I’m sure your answer would be “of course not”, so maybe it’s not so precise after all. In truth, I’d see Mr Fowler’s definition as being more a description of a service locator framework (which, from experience, definitely qualifies as an anti-pattern).

    Perhaps though, the problem lies in me trying to view patterns as generic ideas, rather than specific OO designs. My fear of sticking to the latter is that the march toward ever more functional programming will render many patterns as no more than relics of history. For example, did delegates make the command pattern redundant in C#, or are delegates the way C# implements that pattern? I favour the latter view. Many seem to strongly disagree with me though. Perhaps you are one of them?

Comments are closed.