Abstract
This is a beginner’s tutorial on the Service Locator Pattern. While the Service Locator Pattern is presently very unpopular and pushed aside by the Dependency Injection Container usage, it is still interesting to see what it was offering and why is now considered inadequate.
1. Short tutorial
The goal of this article is to provide a concise tutorial on the “Service Locator Design Pattern” with examples in C#. While this design pattern has been pushed aside by the “Dependency Injection Pattern” and usage of “Dependency Injection Container”, it can still be of interest to readers for both academic reasons and practical reasons since legacy code might still rely on this pattern. Today, the usage of the Service Locator pattern in the new code is discouraged and is even by some authors considered an Anti-Pattern.
The presented code is tutorial level, “demo-of-concept” and for brevity does not handle exceptions, etc.
2. Dependency inversion principle - DIP
So, the “Dependency inversion principle (DIP)” is a SOFTWARE DESIGN PRINCIPLE. It is called “principle” because it provides high-level advice on how to design software products.
DIP is one of five design principles known under the acronym SOLID [3] promoted by Robert C. Martin [5]. The DIP principle states:
- High-level modules should not depend on low-level modules. Both should depend on the abstraction.
- Abstractions should not depend on details. Details should depend on abstractions.
Interpretation
While high-level principle talks about “abstraction”, we need to translate that into terms in our specific programming environment, in this case, C#/.NET. Abstractions in C# are realized by interfaces and abstract classes. When talking about “details”, the principle means “concrete implementations”.
So, that means that DIP promotes the usage of the interfaces in C#, and concrete implementations (low-level modules) should depend on interfaces.
Traditional module dependencies look like this.

DIP proposes this new design.

As you can see, some dependencies (arrows) have inverted directions, so that is where the name “inversion” comes from.
The goal of DIP is to create “loosely coupled” software modules. Traditionally high-level modules depend on low-level modules. DIP has a goal to make high-level modules independent of low-level modules’ implementation details. It does that by introducing an “abstract layer” (in the form of an interface) between them.
The DIP principle is a broad concept and influences other design patterns. For example, when applied to the Factory design pattern or Singleton design pattern, it suggests that those patterns should return a reference to an interface, not a reference to an object. The “Dependency Injection Pattern” follows this principle. The “Service Locator Pattern” follows this principle also.
3. Service locator pattern-static version
First of all, the “Service Locator Pattern” is a SOFTWARE DESIGN PATTERN. It is called a “pattern’ because it suggests low-level specific implementation of a specific problem.
The main problem this pattern aims to solve is how to create “loosely coupled” components. The aim is to improve the modularity of the application by removing dependency between the client and the service implementation. The pattern uses a central registry known as a “service locator” which on request of the client, provides it with the services it depends upon.
There are 4 main roles (classes) in this pattern
- Client: The client is a component/class that wants to use services provided by another component called Service.
- ServiceInterface: The service interface is an abstraction describing what kind of services the Service component is providing.
- Service: The Service component/class is providing services according to the Service-Interface description.
- ServiceLocator: This is a component/class that encapsulates knowledge of how to obtain services that the Client needs/depends upon. It is a single point of contact for the Client to get services. It is a singleton registry for all services that are used by the client. The ServiceLocator is responsible for returning instances of services when they are requested by the Client.
The way it works is Client is dependent on ServiceInterface. The client depends on the ServiceInterface interface but has no dependency on the Service itself. Service implements the ServiceInterface interface and offers certain services that the Client needs. The Client also has a dependency on the ServiceLocator. The Client explicitly requests from the ServiceLocator instance of Service it is dependent upon. Once the Client gets the instance of Service it needs, it can perform work.
Here is a class diagram of this pattern.

Here is a sample code of this pattern.
using System;
internal interface IServiceA
{
void UsefulMethod();
}
internal interface IServiceB
{
void UsefulMethod();
}
internal class ServiceA : IServiceA
{
public void UsefulMethod()
{
//some useful work
Console.WriteLine("ServiceA-UsefulMethod");
}
}
internal class ServiceB : IServiceB
{
public void UsefulMethod()
{
//some useful work
Console.WriteLine("ServiceB-UsefulMethod");
}
}
internal class Client
{
public IServiceA serviceA = null;
public IServiceB serviceB = null;
public void DoWork()
{
serviceA?.UsefulMethod();
serviceB?.UsefulMethod();
}
}
internal class ServiceLocator
{
private static ServiceLocator locator = null;
public static ServiceLocator Instance
{
get
{
// ServiceLocator itself is a Singleton
if (locator == null)
{
locator = new ServiceLocator();
}
return locator;
}
}
private ServiceLocator()
{
}
private IServiceA serviceA = null;
private IServiceB serviceB = null;
public IServiceA GetIServiceA()
{
//we will make ServiceA a singleton
//for this example, but does not need
//to be in a general case
if (serviceA == null)
{
serviceA = new ServiceA();
}
return serviceA;
}
public IServiceB GetIServiceB()
{
//we will make ServiceB a singleton
//for this example, but does not need
//to be in a general case
if (serviceB == null)
{
serviceB = new ServiceB();
}
return serviceB;
}
}
class Program
{
static void Main(string[] args)
{
Client client = new Client();
client.serviceA = ServiceLocator.Instance.GetIServiceA();
client.serviceB = ServiceLocator.Instance.GetIServiceB();
client.DoWork();
Console.ReadLine();
}
}
This version of the pattern is called the “static version” because it uses a field for each service to store an object reference and has a dedicated “Get” method name for each type of service it provides. It is not possible to dynamically add a different type of service to the ServiceLocator. Everything is statically hardcoded.
4. Service locator pattern-Dynamic version-String service names
Here we will show a dynamic version of this pattern, a version with strings used as Service names. The principles are the same as above, just the implementation of ServiceLocator is different.
Here is a class diagram

Here is a sample code of this pattern.
using System;
using System.Collections.Generic;
internal interface IServiceA
{
void UsefulMethod();
}
internal interface IServiceB
{
void UsefulMethod();
}
internal class ServiceA : IServiceA
{
public void UsefulMethod()
{
// some useful work
Console.WriteLine("ServiceA-UsefulMethod");
}
}
internal class ServiceB : IServiceB
{
public void UsefulMethod()
{
// some useful work
Console.WriteLine("ServiceB-UsefulMethod");
}
}
internal class Client
{
public IServiceA serviceA = null;
public IServiceB serviceB = null;
public void DoWork()
{
serviceA?.UsefulMethod();
serviceB?.UsefulMethod();
}
}
internal class ServiceLocator
{
private static ServiceLocator locator = null;
public static ServiceLocator Instance
{
get
{
// ServiceLocator itself is a Singleton
if (locator == null)
{
locator = new ServiceLocator();
}
return locator;
}
}
private ServiceLocator()
{
}
private Dictionary<string, object> registry =
new Dictionary<string, object>();
public void Register(string serviceName, object serviceInstance)
{
registry[serviceName] = serviceInstance;
}
public object GetService(string serviceName)
{
object serviceInstance = registry[serviceName];
return serviceInstance;
}
}
class Program
{
static void Main(string[] args)
{
// register services with ServiceLocator
ServiceLocator.Instance.Register("ServiceA", new ServiceA());
ServiceLocator.Instance.Register("ServiceB", new ServiceB());
// create client and get services
Client client = new Client();
client.serviceA = (IServiceA)ServiceLocator.Instance.GetService("ServiceA");
client.serviceB = (IServiceB)ServiceLocator.Instance.GetService("ServiceB");
client.DoWork();
Console.ReadLine();
}
}
Please note that in this version we have an “initialization phase” in which we register services with ServiceLocator. That can be done from code dynamically or from a configuration file.
5. Service locator pattern - Dynamic version - Generics
Here we will show another dynamic version of this pattern, a version based on generics methods. The principles are the same as above, just the implementation of ServiceLocator is different. This version is very popular in literature [6].
Here is a class diagram

Here is a sample code of this pattern.
using System;
using System.Collections.Generic;
internal interface IServiceA
{
void UsefulMethod();
}
internal interface IServiceB
{
void UsefulMethod();
}
internal class ServiceA : IServiceA
{
public void UsefulMethod()
{
// some useful work
Console.WriteLine("ServiceA-UsefulMethod");
}
}
internal class ServiceB : IServiceB
{
public void UsefulMethod()
{
// some useful work
Console.WriteLine("ServiceB-UsefulMethod");
}
}
internal class Client
{
public IServiceA serviceA = null;
public IServiceB serviceB = null;
public void DoWork()
{
serviceA?.UsefulMethod();
serviceB?.UsefulMethod();
}
}
internal class ServiceLocator
{
private static ServiceLocator locator = null;
public static ServiceLocator Instance
{
get
{
// ServiceLocator itself is a Singleton
if (locator == null)
{
locator = new ServiceLocator();
}
return locator;
}
}
private ServiceLocator()
{
}
private Dictionary<Type, object> registry =
new Dictionary<Type, object>();
public void Register<T>(T serviceInstance)
{
registry[typeof(T)] = serviceInstance;
}
public T GetService<T>()
{
T serviceInstance = (T)registry[typeof(T)];
return serviceInstance;
}
}
class Program
{
static void Main(string[] args)
{
// register services with ServiceLocator
ServiceLocator.Instance.Register<IServiceA>(new ServiceA());
ServiceLocator.Instance.Register<IServiceB>(new ServiceB());
// create client and get services
Client client = new Client();
client.serviceA = ServiceLocator.Instance.GetService<IServiceA>();
client.serviceB = ServiceLocator.Instance.GetService<IServiceB>();
client.DoWork();
Console.ReadLine();
}
}



Join the conversation! Your thoughts help the community grow.