I was reading up on some interview questions on Scott Hanselman’s blog today, and in the comments I saw someone list Singleton and Service Locator as Anti-Patterns, thinking, “Why would someone go against the almighty Martin Fowler?” These are very common patterns of development. I can kind of see how the Singleton Pattern might be an Anti-Pattern–as it forces a dependency on a static method to instantiate the class. However, one of the very reasons that I don’t like this aspect about the Singleton Pattern is because it makes it unusable with some other patterns, like the Service Locator…the Service Locator can’t instantiate it. In any case, I was mildly surprised to see that the Service Locator Pattern was viewed by some as an Anti-Pattern, and wondering how I’d never come across the anti-SL ravings before.
What it seemed to come down to was that–with the Service Locator pattern–you get runtime errors instead of compile-time errors. Even more problematically, if you are using an API designed by someone else, you may not know that you need to configure a JsonSerializer for their library to work (or something to that effect). In such a case, being required to pass a JsonSerializer into the constructor of the API’s object would be more clear. Okay, that makes sense. I can see how being more explicit in what your object requires, especially when developing an API, is useful…and how using the Service Locator might be viewed as a code-smell. I do know that I have a grudge against Zend Framework 2. They seem to haphazardly create services on the fly. It seems that almost every ZF2 object is accessed via the service locator, and it just hasn’t seemed very developer-friendly. So, I can totally get on board with the idea that the Service Locator Pattern can have a code-smell. However, I’m still not sure I’m willing to call it an anti-pattern.
If you’ve ever setup database connection details in a config file (e.g. web.config), then–in essence–you were utilizing the Service Locator Pattern to specify the driver and connection details. To say that connecting to a database is an anti-pattern seems a bit silly. However, it has all the same problems. If you don’t know that the module requires a database connection, so you don’t configure it or you configure it incorrectly, then you get runtime errors instead of compile time errors. So, it has all the same potential for “code smell” that the Service Locator pattern has in terms of external modules, but to my recollection we don’t call connecting to the database an anti-pattern.
Personally, ZF2 aside, I still like using the Service Locator. Call it a personal weakness. When using something like Zend Framework 2, I don’t have control over the instantiation of the Controller classes. So, I can’t inject my services into the constructor of the controller without rewriting Zend. The Service Locator is also extremely useful in handling Convention over Configuration dependencies that can still be overridden with configuration. For instance, given the URL /user/register, we can dynamically get the dependency for the string ‘UserController‘. If none is found, we can look for the namespace ‘Ui.UserController‘, but the developer can easily define ‘UserController‘ to be assigned to ‘Modules.Authentication.Controllers.UserController‘ by explicitly giving the Service Locator a definition of ‘UserController’ so that the router doesn’t have to fallback to the default convention.
In the end, I think ANY pattern can create a code-smell if used carelessly. Imagine the mess you could make by doing almost everything with the Decorator Pattern. Heck, we could even write about all the problems that can arise from inheritance, but the problem isn’t inheritance, which is a useful and needed pattern…the problem is the implementation. So, I’m not ready to write-off the Service Locator quite yet. However, I will try to keep in mind this potential problem, especially if I ever develop an API that is going to require the consumer to provide the implementation.
Kevin Nelson February 10th, 2015
Posted In: Software Design