Is ServiceLocator an anti-pattern?

iamprovidence
4 min readOct 8, 2022

--

Yes, it is. Sorry for not keeping a secret for a bit longer, but there is no much to say. It is a horrible thing, and you should avoid it as much as possible.

However, have you ever thought why it is antipattern? And if there are any cases when it is still an appropriate option for it?

Recently I have been arguing about that with my friend. And I don't really a big fun of arguments like “It makes you depend on ServiceLocator”, “It's like global variables” or “It violates this or that principle”. So I wanted to give it a shot and see why it is so bad.

For those of you who don't know what ServiceLocator is, just have a look at healthy class. All its dependencies are passed through a constructor:

and the one with ServiceLocator. Dependencies are hidden and resolved internally:

Even though looks like DI process simplified, it causes lots of issues.

Now when you know what ServiceLocator is, let's have a look why it is so bad:

1 Run time error instead of compile time

First approach just force you to set all dependencies directly. Even when using such framework like ASP, having all dependencies in constructor allow your DI container to validate graph tree.

With locator, you won't know that service can not be properly instantiated and therefor does not work. Only when you receive an irritated call from a user of your system. In the real word scenario, it would be a real nightmare to maintain hundreds of classes across the project.

2 Lifecycles

Do you remember the last time you wrote Dispose() method in your service? Neither do I. And that’s because DI container usually create and dispose services by itself.

With ServiceLocator you will be forced to maintain lifecycles of services by your own. Are you not scared yet? Imagine disposing a dependency that is used by another service. Imagine disposing a dependency that is used by another thread. Imagine disposing dependency with different lifecycle. What about now? If I were you, I would just surrender, and allow my services to live as much as they want 😃

3 Unit tests

I can see myself mocking dependencies in the first example. Now try doing that in the second one.

You might think, “if there is a default constructor, it should work?” I won’t be so sure about that.

There is no help from IntelliSense, so you need to open that class and discover its dependencies. Glad it is your code. But image if some third-party library decided to use ServiceLocator. Pure horror. Just try to figure out what you should mock.

“It is not so complicated. Just navigate to a class and see”, you might think.

So here we are. Now everything mocked.

Since ServiceLocatoris static, we need to reset decencies for each unit tests. Surprise-surprise, parallel execution will also be an issue.

We can try to fix it by injecting ServiceLocator instead of having static instance:

A little better for unit tests. No explicit reset and unit tests independent of each other. But does it really help?

Still, IntelliSense is useless. We know that service depends on locator, but that's it. Good luck to figure out what services you need. Moreover, add another dependency to your service and all tests will fail 😬

Talking about disadvantages. However, can we actually use it? I know a lot of crap has been said about it, but there are still some places where I can justify usage of it.

1 Legacy code

We all have to deal with legacy code. Although there are tons of tips, best practices and patterns, sometimes it just not appropriate to spend time and refactor everything. Or you may just get lazy as I do 😃 So when refactoring large legacy applications instead of redesign it, just use ServiceLocator. And then fix it latter. Just remember, how do you eat an elephant? One bite at a time!

2 Frameworks limitation

Every man has his besetting sin. No matter how you like your framework, it was made by a human, the same as you, from skin and bones. And even framework’s developer sometimes put us in an awkward position and force us to work with unfriendly АРІ.

One of those examples could be BackgroundService in ASP. It is a special service for starting Jobs. However, the behavior of that service is also special. It registered as Singleton which means we can not inject Scoped services there as you would do with Controllers. The solution would be to use ServiceLocator and instantiate needed dependencies on a fly.

Such approach is not ideal, but at least you would not write unit-tests for framework code, therefore a little less headaches for you.

Summary

The problem with using a Service Locator isn’t that you take a dependency on a particular ServiceLocator.

It just gives a horrible developer experience by creating a terrible API that require a lot of boilerplate code to support.

It not only requiring you to deeply look into the code to figure out how dependencies are handled, but also hiding some DI issues, like circular dependencies. Those errors appear only in runtime, which provoke tons of bugs.

As a result, usage of ServiceLocator dramatically increase maintenance of your code, your tests and makes you focus on DI bugs instead of doing something important.

What are your thoughts on ServiceLocator? What impediment you faced with it?💬

Give this article a clap or a few if you like it 👏

Support me with a link below ☕️

And don't forget to follow to receive more ✅

--

--

iamprovidence

👨🏼‍💻 Full Stack Dev writing about software architecture, patterns and other programming stuff https://www.buymeacoffee.com/iamprovidence