Service Locator Design Pattern

This article assumes a basic knowledge of de-coupled architecture. An understanding of various design patterns will help you to understand this better.

Maybe the term "Service Locator" is new to you but it's not hard to understand. This article assumes a basic knowledge of de-coupled architecture. An understanding of various design patterns will help you to understand this better.

We know those software components are subject to change depending on the business. Since software is for business and the reverse is not always true. If so, then we have to keep in mind that, when change comes, our application can adapt smoothly or at least by giving only a little trouble.

This is the advantage of de-coupled architecture and every software organization expects it in their application.

Fine, we have learned the concept of de-coupling, now how to do it? There are many ways, the truth to be told is “It will take a few hours to understand de-coupling but will take a few years to master it”. One of the nice traditional approaches to implement de-coupled is by IoC. IoC stands for Inversion of Control.

The pattern suggests not creating an object of one class within another class, if we do then it will be tightly dependent on each other.

So, here we will invert the traditional communication of two objects and that results in the name.

Ok, again we can implement IoC in two ways, one using a Service Locator and another is Dependency Injection.

Dependency Injection

So in this example what we will implement is one part of IoC or Inversion of Control. The Service Locator is a pattern by which we can reduce the dependency of one object on another that we will see shortly and Dependency injection (DI) is another smart solution for the same problem. Anyway we are not interested in talking about DI here.

Again we can implement a Service Locator in one of the following two ways.

Service Locator in two ways

One is a strong type and another one is a generic type. In this article we will implement both of them.

Fine, now let's understand theory and implementation of service location. Have a look at the following diagram. The classA is consuming both ServiceA and ServiceB and for that we are creating an instance of ServiceA and ServiceB within ClassA and this is the example of Dependency.

Dependency

Now the solution is something like this. We have implemented one layer to consume the service that is nothing but a service locator. Now, ClassA does not know about the Object creation mechanism to consume ServiceA and ServiceB.
 
consume ServiceA and ServiceB
So, this pattern helps to locate a service and hence the name. The following is a summary of the objective of a Service locator.
  • You want to decouple your classes from their dependencies so that these dependencies can be replaced or updated with little or no change to the classes.
  • You want to write logic that depends on classes whose concrete implementation is not known at compile time.
  • You want to be able to test your classes in isolation, without the dependencies.
  • You do not want the logic that locates and manages the dependencies to be in your classes.
  • You want to divide your application into loosely coupled modules that can be independently developed, tested, versioned and deployed.

Now, let me disclose the fact. It's a common question, “Is a service locator really a good pattern?”. Many say yes, many no and its anti-pattern means it has its own disadvantages and advantages so we should avoid it. Anyway we will not spend more words on this topic, in another article I am will present it.

Now, let's start with a Strong type service locator and implement it.

Strong type service locator

In a strong type service locator the service locator class will return a known type. Have a look at the following example. Here we have implemented the LoggingService class where we have implemented an IService Interface.

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. namespace Client  
  5. {  
  6.     public interface IService  
  7.     {  
  8.         void ExecuteService();  
  9.     }  
  10.     public class LoggingService : IService  
  11.     {  
  12.         public void ExecuteService()  
  13.         {  
  14.             Console.WriteLine("Executing Log Service");  
  15.         }  
  16.     }  
  17.   
  18.     public static class ServiceLocator  
  19.     {  
  20.         public static IService ObjService = null;  
  21.           
  22.         //Service locator function returning strong type   
  23.         public static IService SetLocation(IService tmpser)  
  24.         {  
  25.             if (ObjService == nullreturn new LoggingService();  
  26.             return ObjService;  
  27.         }  
  28.           
  29.         //Execute service  
  30.         public static void ExecuteService()  
  31.         {  
  32.             ObjService.ExecuteService();  
  33.         }  
  34.     }  
  35.   
  36.     class Program  
  37.     {  
  38.         static void Main(string[] args)  
  39.          {  
  40.            IService svr =  ServiceLocator.SetLocation(new LoggingService());  
  41.            svr.ExecuteService();  
  42.            Console.ReadLine();  
  43.         }  
  44.     }  

Have a look at the ServiceLocator class. This class contains a SetLocator() function that takes one strong type argument (IService) and in return it returns an object of LoggingService that implements an IService interface.

The function of ExecuteService() is very simple, it just executes the appropriate function of the concrete class. Since the service locator class returns one string type, hence the name. Here is output of the example above.

output

Generic type service locator

This is another type of service locator, the difference between strong and Generic type is that it can deal with various types since this is generic in nature. Have a look at the following example.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. namespace Client  
  5. {  
  6.     public interface IServiceA  
  7.     {  
  8.         void Execute();  
  9.     }  
  10.   
  11.     public class ServiceA : IServiceA  
  12.     {  
  13.         public void Execute()  
  14.         {  
  15.             Console.WriteLine("A service called.");  
  16.         }  
  17.     }  
  18.   
  19.     public interface IServiceB  
  20.     {  
  21.         void Execute();  
  22.     }  
  23.   
  24.     public class ServiceB : IServiceB  
  25.     {  
  26.         public void Execute()  
  27.         {  
  28.             Console.WriteLine("B service called.");  
  29.         }  
  30.     }  
  31.   
  32.     public interface IService  
  33.     {  
  34.         T GetService<T>();  
  35.     }  
  36.     public class ServiceLocator : IService  
  37.     {  
  38.         public Dictionary<objectobject> servicecontainer = null;  
  39.         public ServiceLocator()  
  40.         {  
  41.             servicecontainer = new Dictionary<objectobject>();  
  42.             servicecontainer.Add(typeof(IServiceA), new ServiceA());  
  43.             servicecontainer.Add(typeof(IServiceB), new ServiceB());  
  44.         }  
  45.         public T GetService<T>()  
  46.         {  
  47.             try  
  48.             {  
  49.                 return (T)servicecontainer[typeof(T)];  
  50.             }  
  51.             catch (Exception ex)  
  52.             {  
  53.                 throw new NotImplementedException("Service not available.");  
  54.             }  
  55.         }  
  56.     }  
  57.     class Program  
  58.     {  
  59.         static void Main(string[] args)  
  60.          {  
  61.             ServiceLocator loc = new ServiceLocator();  
  62.             IServiceA Aservice =  loc.GetService<IServiceA>();  
  63.             Aservice.Execute();  
  64.   
  65.             IServiceB Bservice = loc.GetService<IServiceB>();  
  66.             Bservice.Execute();  
  67.   
  68.            Console.ReadLine();  
  69.          }  
  70.     }  

The implementation is a little tricky with a generic function, if you are new to generic functions then I suggest you implement a few examples of a generic function for a better understanding. In this example our generic function takes an argument of type T and then it's checking the type within the dictionary if it is present then it will return an object of the concrete class.

We are populating a dictionary within the constructor. Here is sample output.
 
constructor

Conclusion

In this article we have learned how to implement two types of service locators with an example. I hope you have understood both and it's advantages. As we said, people's opinion is different of the service locator pattern. Some think it's a good pattern and some not, so I have decided to write another article to discuss both the pros and cons.