13 August, 2012

Implementing the Service Locator Pattern

The idea of a service locator is to have a single point of reference to obtain a service needed by an application. The service locator acts as a registry for services and provides functionality for registering and retrieving references to services.

A service locator implementation consists of three components:

  1. Service interface(s)
  2. Service implementation(s)
  3. Service locator

The picture below illustrates how the service locator, the interfaces (I1, I2 and I3) and the service implementations (Service 1, Service 2 and Service 3) are connected.


The following illustrates an example of how the service locator pattern can be implemented.

All interfaces implement a global interface called IService:
public interface IService { }
This example uses two services Products and Users. For each service an interface is defined:
// Interface for product service
public interface IProductService : IService
{
    // Get all products
    List<Product> GetProducts();
}

// Interface for user service
public interface IUserService : IService
{
    // Get all products
    List<User> GetUsers();
}
The service implementations implement the interfaces:
public class ProductService : IProductService
{
    // Get all products
    List<Product> GetProducts()
    {
        // Return List of products
    }
}

public class UserService : IUserService
{
    // Get all products
    List<User> GetUsers()
    {
        // Return list of users
    }
}
The service locator is implemented using the singleton pattern and provides a registry for registering and retrieving service references. Services a registered using the service interfaces:
public class ServiceLocator
{
    // Service Locator
    private static ServiceLocator _serviceLocator;
        
    // Lock to make singleton pattern thread safe
    private static object _lock = new object();

    // Dictionary of all available services
    private Dictionary<Type, IService> _services;

    // Constructor to initialize all services
    private ServiceLocator() 
    {
        _services = new Dictionary<Type, IService>();
        _services.Add(typeof(IProductService), new ProductService());
        _services.Add(typeof(IUserService), new UserService());
    }

    // Get service locator
    public static ServiceLocator GetServiceLocator()
    {
        lock (_lock)
        {
            if (_serviceLocator == null)
                _serviceLocator = new ServiceLocator();
        }
        return _serviceLocator;
    }

    // Get service of type T
    public T GetService()
    {
        return (T)_services[typeof(T)];
    }
}
To receive a reference to a service through the service locator, the application calls the generic GetService method on the service locator:
var serviceLocator = ServiceLocator.GetServiceLocator();
var productService = serviceLocator.GetService<IProductService>();
var products = productService.GetProducts();
The service locator pattern reduces the dependency on the implementation and makes it easy to substitute service implementation without changing the entire application. In this case if products were read from a database, the implementation could be changed to read from an XML file by simply changing the service implementation.

No comments:

Post a Comment