How to implement the ServiceLocator design pattern

The ServiceLocator design pattern builds loosely coupled modules that can be independently developed, tested, and deployed seamlessly

ServiceLocator

ServiceLocator

Design patterns are solutions to recurring problems and complexities in software design. The ServiceLocator design pattern promotes loose coupling but sans the need of injecting dependencies through constructors, properties or interfaces. This is a popular design pattern that can be used to decouple the service consumers from the concrete classes implementing such services.

The ServiceLocator is responsible for returning instances of services when they are requested for by the service consumers or the service clients. In essence, the service consumers or the service clients are not aware of the types that have the service actually implemented in them, rather, the service clients need to take advantage of the service locator to retrieve a reference object to the actual service implementation class.

The components participating in the ServiceLocator design pattern include the following:

  1. ServiceLocator -- The ServiceLocator abstracts the creation of the dependencies and if might also act as a repository of service objects that have already been created and initialized.
  2. Initializer -- This is responsible for initializing the service instances at runtime. Once these objects have been initialized, they are stored in the ServiceLocator.
  3. Client -- This represents the service consumer or the service client.
  4. Service(s) -- This represents the actual service contracts and their implementations.

Implementing the ServiceLocator pattern

In this section we will explore how we can implement the ServiceLocator design pattern in C#. Let’s now dig into a bit of code. To get started, create a WCF Application Project in Visual Studio IDE. Next, create two services named, CustomerService and ProductService.

Here’s how the service contracts look like -- I’ve incorporated a simple OperationContract (service operation) in each of these services.

[ServiceContract]

   public interface ICustomerService

   {

       [OperationContract]

       string GetText();

   }

[ServiceContract]

   public interface IProductService

   {

       [OperationContract]

       string GetText();

   }

The implementation of the ProductService and CustomerService classes is given below. Note that these service implementation classes implement the IProductService and ICustomerService interfaces (service contracts) respectively.

public class ProductService : IProductService

   {

       public string GetText()

       {

           return "This is the Product Service...";

       }

   }

public class CustomerService : ICustomerService

   {

       public string GetText()

       {

           return "This is the Customer Service...";

       }

   }

And, now, we would create the ServiceLocator class. This class would be static in nature and would contain a Dictionary to store the service instances. Besides, the ServiceLocator class would also contain the GetService and RegisterService methods. While the former will be used to retrieve a particular service instance, the latter would be used to store services instances inside the ServiceLocator’s Dictionary instance named “registeredServices”.

Here's the complete source code of the ServiceLocator class.

public static class ServiceLocator

   {

       private static readonly Dictionary<Type, object> registeredServices = new Dictionary<Type, object>();

       public static T GetService<T>()

       {

           return (T)registeredServices[typeof(T)];

       }

       public static void RegisterService<T>(T service)

       {

           registeredServices[typeof(T)] = service;

       }

       public static Int32 Count

       {

           get { return registeredServices.Count; }

       }

   }

Now that we have created a simple ServiceLocator class, let’s explore how we can take advantage of it to locate and use services. Here's how you can use the ServiceLocator class we just created -- I’ve used a console application here to demonstrate how the services can be located with simplicity. You can feel free to use the same code anywhere in your application where you would need to register and then locate services.

static void Main(string[] args)

       {

           ICustomerService customerService = new CustomerService();

           IProductService productService = new ProductService();

           ServiceLocator.RegisterService(customerService);

           ServiceLocator.RegisterService(productService);

           ICustomerService testService = ServiceLocator.GetService<ICustomerService>();

           string msg = testService.GetText();

           Console.WriteLine(msg);

           Console.Read();

       }

Note that in this example I've not used a separate class to represent the Initializer component we discussed earlier since the initialization code has been embedded inside the ServiceLocator class.

The ServiceLocator design pattern is a good choice when you would want to decouple your classes from their dependencies but with minimal change in your application's source code. Another good candidate for implementing this design pattern is when you would want to isolate the dependencies and especially when these dependencies are not known at the compile time. In other words, you can leverage the ServiceLocator design pattern when you need to design classes that in turn depend on classes whose concrete implementations aren't known at compile time.

This article is published as part of the IDG Contributor Network. Want to Join?

To comment on this article and other InfoWorld content, visit InfoWorld's LinkedIn page, Facebook page and Twitter stream.
From CIO: 8 Free Online Courses to Grow Your Tech Skills
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.