Table of Contents Show
Singletons are often described as an antipattern. Because they often create more problems than they solve.
On the other hand, they are easy to create, everyone understands them, and they usually do solve a very concrete problem.
A service locator is a good solution for keeping a simple approach while reducing a few negative side effects of singletons.
Implementing a service locator
A service locator usually has three tasks:
- Register services: add an instance of a specific service type to the service locator.
- Unregister services: remove the current service of a specific type from the service locator.
- Get services: get the current service of a specific type.
My implementation uses the following definition. It uses a dictionary that maps types to objects. You can find the full implementation on Github.
public class ServiceLocator
{
public void Register<T>(T instance)
{
...
}
public void Unregister<T>(T instance)
{
...
}
public T Get<T>()
{
...
}
}
Service locators can be implemented in different variations. For example, it’s possible to support multiple services of the same type. This implementation uses a singleton for the service locator itself and all services have to implement an IGameService interface.
Using service locators in Unity
I like to use the component-based approach of Unity and assemble my app in a scene. If I want to use a specific service in my app, I just want to drag it into the scene (or a prefab).
Therefore, I usually let the services register themselves:
public class LocalFileStorage : IFileStorage
{
void Awake()
{
ServiceLocator.Register<IFileStorage>(this);
}
void OnDestroy()
{
ServiceLocator.Unregister<FileStorage>(this);
}
}
What happens if multiple services are added to the scene? In my implementation, I just take the latest service.
Advantages over singletons
Service locators work with interfaces instead of concrete implementations. This provides a lot of flexibility.
In your test setup, you can replace your default implementation with a mock object. For example, your DataService returns some dummy data instead of downloading data from a server.
You can use different services for different platforms or environments. Mobile devices might use different views than desktops. Services for paid tiers usually provide more features than free tiers.
Common pitfalls
A service locator still provides global access to an object. This introduces similar problems as with static variables or singletons. Every part of the system can retrieve a service and access or even modify data.
Therefore, you should not add everything to the service locator. Really stick to a handful of services, which are useful for the whole application.
You can also use multiple service locators for multiple parts of your software.