Archive for July, 2015

TheServiceLocatorPattern

Author: Cole Francis, Architect

BACKGROUND:

In object-oriented programming (OOP), the Dependency Inversion Principle, or DIP, stipulates that the conventional dependency relationships established from the high-level policy-setting modules, to the low-level dependency modules, are inverted (i.e. reversed), creating an obvious layer of indirection used to resolve component dependencies. Therefore, the high-level components should exist independently from a low-level component’s implementation and all its minutia.

DIP was suggested by Robert C. Martin in a paper he wrote in 1996 titled, Object Oriented Design Quality Metrics: an analysis of dependencies. Following that, there was an article that appeared in the C++ Report in May 1996 entitled “The Dependency Inversion Principle” and the books Agile Software Development, Principles, Patterns, and Practices, and Agile Principles, Patterns, and Practices in C#.

The principle inverts the way most people may think about Object Oriented Design (OOD), and the Service Locator pattern is an excellent pattern to help demonstrate DIP principles, mainly because it facilitates a runtime provisioning of chosen low-level component implementations from its higher-level componentry.

The key tenants of the Service Locator Pattern are (in layman’s terms):

  • An interface is created, which identifies a set of callable methods that the concrete service class implements.
  • A concrete service class is created, which implements the interface. The concrete class is the component where all the real work gets done (e.g. calling a database, calling a WCF method, making an Http POST Ajax call, etc…).
  • A Service Locator class is created to loosely enlist the interface and its corresponding concrete service class. Once a client application requests to resolve an enlisted type, then it’s the Service Locator’s job to resolve the appropriate interface and return it to the calling application so that the service class’s method(s) can be called.
  • A visual representation of the Dependency Inversion Pattern

To help explain the pattern a little more clearly, I’ve put together a working example of the Service Locator Pattern implementing mock services. One call simulates the retrieval of credit card authorization codes for fulfilled orders coming from a database. Once I retrieve the authorization codes, I then simulate settling them using a remote credit card service provider. Afterwards, I mimic updating our database records with the credit card settlement codes that I received back from the credit card service provider. I’ve intentionally kept the following example simple so that it’s easy to follow and explain, and I’ve also broken the code down into color-coded sections to further dissect the responsibility of each region of code:


namespace ServiceLocatorExample
{
    /// 
    /// A textbook implementation of the Service Locator Pattern.  
    /// 
    /// 
    public class ServiceLocator : IServiceLocator
    {
        #region Member Variables

        /// 
        /// An early loaded dictionary object acting as a memory map for each interface's concrete type
        /// 
        /// 
        private IDictionary services;

        #endregion

        #region IServiceLocator Methods

        /// 
        /// Resolves the concrete service type using a passed in interface
        /// 
        /// 
        public T Resolve()
        {
            try
            {
                return (T)services[typeof(T)];
            }
            catch (KeyNotFoundException)
            {
                throw new ApplicationException("The requested service is not registered");
            }
        }

        /// 
        /// Extends the service locator capabilities by allowing an interface and concrete type to 
        /// be passed in for registration (e.g. if you wrap the assembly and wish to extend the 
        /// service locator to new types added to the extended project)
        /// 
        /// 
        /// IDictionary(object, object), where the first parameterized object is the service interface 
        /// and the second parameterized object is the concrete service type
        /// 
        /// 
        public void Register(object resolver)
        {
            try
            {
                this.services[typeof(T)] = resolver;
            }
            catch (Exception)
            {
                
                throw;
            }
        }

        #endregion

        #region Constructor(s)

        /// 
        /// The service locator constructor, which resolves a supplied interface with its corresponding concrete type
        /// 
        /// 
        public ServiceLocator()
        {
            services = new Dictionary();

            // Registers the service in the locator
            this.services.Add(typeof(IGetFulfilledOrderCCAuthCodes), new GetFulfilledOrderCCAuthCodes());
            this.services.Add(typeof(IGetFulfilledOderCCSettlementCodes), new GetFulfilledOderCCSettlementCodes());
            this.services.Add(typeof(IUpdateFulfilledOrderCCSettlementCodes), new UpdateFulfilledOderCCSettlementCodes());
        }

        #endregion
    }
}


PRE-LOADING DECOUPLED RELATIONSHIPS TO A DICTIONARY OBJECT AT RUNTIME:

If you look at the all the sections I’ve highlighted in yellow, all I’m doing is declaring a Dictionary Object to act as a “registry placeholder” in the Member Variables Region, and then I’m preloading the interface and service class as a key/value pair to the service registry in the Constructor(s) Region of the code.

The key/value pairs that get stored in the Dictionary Object loosely describes the concrete class and its corresponding interface that gets registered as service objects (e.g. “IMyClass”, “MyClass”). An interface describes the methods and properties that are implemented in the concrete class, and the concrete class is type where all the real work gets accomplished. In its most primitive form, the primary job of ServiceLocator class is to store key/value pairs in a simple Dictionary object and either register or resolve those key/value pairs whenever it’s called upon to do so.

GETTING AND SETTING VALUES IN THE DICTIONARY OBJECT AT RUNTIME:

The section that’s color-coded in green denotes simple getter and setter-like methods that are publicly exposed to a consuming application, allowing that consuming application to either register new service objects in the Dictionary Object registry or resolve an existing service object in the Dictionary Object’s registry for use in a client application.

In fact, listed below is a textbook example of how a client application would resolve an existing service object in the Dictionary Object’s registry for use. In this example I’m resolving the IGetFulfilledOrderCCAuthCodes interface to its concrete type and then calling its GetFulfilledOrderCCAuthCodes() method using a console application that I quickly threw together…


/// 
/// Gets the fulfilled orders credit card authorization codes to settle on
/// 
/// 
private static List GetFulfilledOrderCCAuthCodes()
{
    ServiceLocatorExample.ServiceLocator locator2 = new ServiceLocatorExample.ServiceLocator();
    IGetFulfilledOrderCCAuthCodes o = locator2.Resolve();
    return o.GetFulfilledOrderCCAuthCodes();
}


CONGRATULATIONS! YOU’RE DONE:

Assuming that someone has already written logic to retrieve the fulfilled order authorization codes from the database, then your part is done! I really wish there was more to it than this so that I would look like some sort of architectural superhero, but alas there isn’t. Thus, if all you were looking to get out of this post is how to implement a textbook example of the Service Locator design pattern, then you don’t need to go any further. However, for those of you who want to know the advantages and disadvantages of the Service Locator design pattern then please keep reading:

THE ADVANTAGES:

  • The Service Locator Pattern follows many well-recognized architectural principles, like POLA, Hollywood, KISS, Dependency Inversion, YAGNI, and others…
  • Although the Service Locator Pattern is not considered to be a lightweight pattern, it’s still very simple to learn and is easily explainable to others, which means that your junior developer’s eyes won’t pop out of their heads when you attempt to explain the concept to them.
    • This truly is a framework pattern that you can teach a less knowledgeable person over a lunch hour and expect them to fully understand when you’re done, because the Service Locator framework wires everything up using an minimal number of resources (e.g. a Dictionary object containing key/value pairs and the ability to read from and (optionally) write to the Dictionary object).
  • The Service Locator design pattern allows you to quickly and efficiently create a loosely coupled runtime linker that’s ideal for separating concerns across the entire solution, as each type is concerned only about itself and doesn’t care what any of the other components do.
  • For you architectural purists out there, just be aware that using the Service Locator design pattern doesn’t preclude you from coupling it with good Dependency Injection and Factory Pattern frameworks. In fact, by doing so you have the potential of creating a lasting framework that meets the conditions of both SOLID and POLA design principles, as well as many others. Perhaps I’ll make this the topic of my next architectural discussion…

THE DISADVANTAGES:

  • The services (i.e. Key/value pairs that represent concrete classes) that get registered in the Service Locator object are often considered “black box” items to consumers of the Service Locator class, meaning service objects and their dependencies are completely abstracted from the applications that call it. This loosely coupled structure makes it extremely difficult to track down issues without development access to the source code for not only the Service Locator itself, but also all of the dependent service objects that get registered by it at runtime.
    • If you find yourself in this situation and you don’t have access to the Service Locator source code, then tools like the Microsoft ILDasm or the RedGate’s .NET Reflector are occasionally able to shed some light on what’s going on inside the Service Locator assembly; however, if the code happens to be obfuscated or if hidden dependencies are completely unresolvable, then deciphering issues can become an exercise in futility. For this very reason, the Service Locator Pattern violates ACID Principles, which is why some architectural gurus consider the Service Locator design to be more of a design anti-pattern.
  • Because Service Locator’s dictionary is constructed using a key/value concept, all key names must be unique, meaning that it’s not very well-suited for distributed systems without adding more validation checks around the Register method’s dictionary insertion code.
  • Testing the registered objects becomes a difficult process because we aren’t able to test each object in isolation, as registered service objects are considered “black box” items to both the Service Locator and its callers.
  • As I previously mentioned, objects are “late-bound”, which means that a caller is going to burn up some CPU cycles waiting the Service Locator to find the service’s registry entry in the Dictionary object and return it to them, and then they still have to invoke that object “late-bound”. Sure the time is minimal, but it’s still time that isn’t being spent on enhancing something more valuable…like the user experience.
  • There are also some concerns from a security standpoint. Keep in mind that my ServiceLocator class allows people to dynamically register their own objects at runtime (see the code snippet below). Who knows what types of malicious service objects might get registered? What if our ServiceLocator class only performed a minimal set of validations before executing a method on a service object? Now that’s an awfully scary thought.



/// 
/// Extends the service locator capabilities by allowing an interface and concrete type to 
/// be passed in for registration (e.g. if you wrap the assembly and wish to extend the 
/// service locator to new types added to the extended project)
/// 
///
public void Register(object resolver)
{
    try
    {
       this.services[typeof(T)] = resolver;
    }
    catch (Exception)
    {
       throw;
    }
}



SOME FINAL TALKING POINTS:

As you can see, the Service Locator Design Pattern is very simple to explain and understand, but it certainly isn’t a “one size fits all” design pattern, and a fair degree of caution should be exercised before choosing it as a framework to build your solution on. You might even consider coupling it or substituting it with a more robust pattern, like one that offers the same capabilities but resolves its dependency objects at build-time instead of runtime (e.g. Dependency Injection).

Personally, I think this pattern’s “sweet spot” is for applications that: (1) Have a very narrow design scope; (2) Are built for in-house use and not third-party distribution; (3) Leverage service objects that are well-known and well-tested by a development team; (4) Aren’t designed for blazing speed. Thanks for reading and keep on coding! 🙂