Search This Blog

2015/12/19

Design Pattern - Lazy Singletone Service Locator Implementation

 Service locator design pattern is a pattern that is used to remove direct dependency of service client on service provider.

Main idea here is building a centralized repository of services then client will get required service from repository this way we will decouple service implementation & Service clients.

To demonstrate this pattern we will create a console application say (MyConsoleApp).
Then add a class (say MyEntity.cs).

Below is my code from MyEntity.cs.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace MyConsoleApp
{
    //helper class
    public class Utils
    {
        public static string RandomString(int maxSize)
        {
            char[] chars = new char[62];
            chars =
            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
            byte[] data = new byte[1];
            using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
            {
                crypto.GetNonZeroBytes(data);
                data = new byte[maxSize];
                crypto.GetNonZeroBytes(data);
            }
            StringBuilder result = new StringBuilder(maxSize);
            foreach (byte b in data)
            {
                result.Append(chars[b % (chars.Length)]);
            }
            return result.ToString();
        }
    }

    //interface
    public interface IAddIntService
    {
        int Add(int param1,int param2);
        string ServiceInstanceId
        {
            get;
            set;
        }
        DateTime InstatiateTime
        {
            get;
            set;
        }
    }

    public interface IAddLongService
    {
        long Add(long param1,long param2);
        string ServiceInstanceId
        {
            get;
            set;
        }
        DateTime InstatiateTime
        {
            get;
            set;
        }
    }

    public interface IAddDoubleService
    {
        double Add(double param1, double param2);
        string ServiceInstanceId
        {
            get;
            set;
        }
        DateTime InstatiateTime
        {
            get;
            set;
        }
    }

    //implementation
    class AddInt : IAddIntService
    {
        //constructor
        public AddInt()
        {
            this.ServiceInstanceId = Utils.RandomString(15);
            this.InstatiateTime = DateTime.Now;
        }

        public string ServiceInstanceId
        {
            get;
            set;
        }
        public DateTime InstatiateTime
        {
            get;
            set;
        }

        public int Add(int param1, int param2)
        {
            return Convert.ToInt32(param1 + param2);
        }
    }

    class AddLong : IAddLongService
    {
        public AddLong()
        {
            this.ServiceInstanceId = Utils.RandomString(15);
            this.InstatiateTime = DateTime.Now;
        }
        public DateTime InstatiateTime
        {
            get;
            set;
        }
        public long Add(long param1, long param2)
        {
            return Convert.ToInt64(param1 + param2);
        }
        public string ServiceInstanceId
        {
            get;
            set;
        }
    }
   
    class AddDouble : IAddDoubleService
    {
        public AddDouble()
        {
            this.ServiceInstanceId = Utils.RandomString(15);
            this.InstatiateTime = DateTime.Now;
        }
        public DateTime InstatiateTime
        {
            get;
            set;
        }
        public double Add(double param1, double param2)
        {
            return Convert.ToDouble(param1 + param2);
        }
        public string ServiceInstanceId
        {
            get;
            set;
        }
    }
}

Here I am creating three interfaces
1)  IAddIntService:It has method to implement
    int Add(int param1,int param2);
2)  IAddLongService:It has method to implement
    long Add(long param1,long param2);
3)  IAddDoubleService :It has method to implement
      double Add(double param1, double param2);
      Each interface has two additional properties
       a) InstatiateTime: Holds time at which object created
       b) ServiceInstanceId: Holds instance id i.e. random 15 digited string to differentiate instance created approximately at same time.

Interfaces above are implemented as follows
1)AddInt class implements IAddIntService
2)AddLong class implements IAddLongService
3)AddDouble class implements IAddDoubleService
 
At the time of instantiation of these three class objects in respective constructor I am initializing properties ServiceInstanceId & InstatiateTime.


Now Let’s add one more class file say “MyPattern.cs
Below is code in MyPattern.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace MyConsoleApp
{
    public interface IServiceLocator
    {
        T GetService<T>();

        DateTime InstatiateTime
        {
            get;
            set;
        }
        string ServiceInstanceId
        {
            get;
            set;
        }
    }

    //service instance eager loaded
    class ServiceLocator : IServiceLocator
    {
        //repository to maintain list of available services
        private IDictionary<object, object> services;

        //constructor
        internal ServiceLocator()
        {
            this.InstatiateTime = DateTime.Now;
            this.ServiceInstanceId = Utils.RandomString(15);
            services = new Dictionary<object, object>();

            //list of available services
            this.services.Add(typeof(IAddIntService), new AddInt());
            this.services.Add(typeof(IAddLongService), new AddLong());
            this.services.Add(typeof(IAddDoubleService), new AddDouble());
        }

        public T GetService<T>()
        {
            try
            {
                return (T)services[typeof(T)];
            }
            catch (KeyNotFoundException)
            {
                throw new ApplicationException("The requested service is not registered");
            }
        }

        //property implemented
        public DateTime InstatiateTime
        {
            get;
            set;
        }

        public string ServiceInstanceId
        {
            get;
            set;
        }
    }

    //service instance lazy loaded
    internal class LazyServiceLocator : IServiceLocator
    {
        //repository that keeps maps of service type & interface that it implements so that when required we can instantiate object of desired type
        private IDictionary<Type, Type> services;

        //repository that keeps maps of service type object instatiated & interface that it implements so when available no need to reinstantiate
        private IDictionary<Type, object> instantiatedServices;

        //constructor
        internal LazyServiceLocator()
        {
            this.InstatiateTime = DateTime.Now;
            this.ServiceInstanceId = Utils.RandomString(15);

            //initialize repositories
            this.services = new Dictionary<Type, Type>();
            this.instantiatedServices = new Dictionary<Type, object>();

            this.BuildServiceTypesMap();
        }

        private void BuildServiceTypesMap()
        {
            this.services.Add(typeof(IAddIntService), typeof(AddInt));
            this.services.Add(typeof(IAddLongService), typeof(AddLong));
            this.services.Add(typeof(IAddDoubleService), typeof(AddDouble));
        }

        public T GetService<T>()
        {
            if (this.instantiatedServices.ContainsKey(typeof(T)))
            {
                //found in earlier instantiated objects
                return (T)this.instantiatedServices[typeof(T)];
            }
            else
            {
                // lazy initialization
                try
                {
                    // use reflection to invoke the service
                    ConstructorInfo constructor = services[typeof(T)].GetConstructor(new Type[0]);

                    T service = (T)constructor.Invoke(null);

                    // add the service to the ones that we have already instantiated
                    instantiatedServices.Add(typeof(T), service);

                    return service;
                }
                catch (KeyNotFoundException)
                {
                    throw new ApplicationException("The requested service is not registered");
                }
            }
        }

        //property implemented
        public DateTime InstatiateTime
        {
            get;
            set;
        }

        public string ServiceInstanceId
        {
            get;
            set;
        }
    }

    //service locator SingleTon
    internal class LazySingleTonServiceLocator : IServiceLocator
    {
        //repository that keeps maps of service type & interface that it implements so that when required we can instantiate object of desired type
        private IDictionary<Type, Type> services;

        private static readonly object TheLock = new Object();

        //stores the instance of service locator that will be instantiated only once then reused
        private static IServiceLocator ServiceLocatorInstance;

        //repository that keeps maps of service type object instatiated & interface that it implements so when available no need to reinstantiate
        private readonly IDictionary<Type, object> instantiatedServices;

        //private constructor so service locator instance can not be created by others than GetServiceLocatorInstance method
        private LazySingleTonServiceLocator()
        {
            this.InstatiateTime = DateTime.Now;
            this.ServiceInstanceId = Utils.RandomString(15);

            //initialize repositories
            this.services = new Dictionary<Type, Type>();
            this.instantiatedServices = new Dictionary<Type, object>();

            this.BuildServiceTypesMap();
        }

        private void BuildServiceTypesMap()
        {
            this.services.Add(typeof(IAddIntService), typeof(AddInt));
            this.services.Add(typeof(IAddLongService), typeof(AddLong));
            this.services.Add(typeof(IAddDoubleService), typeof(AddDouble));
        }

        //static method to retreive service locator instance
        public static IServiceLocator GetServiceLocatorInstance
        {
            get
            {
                lock (TheLock) // thread safety
                {
                    if (ServiceLocatorInstance == null)
                    {
                        ServiceLocatorInstance = new ServiceLocator();//private constructor accessible from same class only
                    }
                }

                return ServiceLocatorInstance;
            }
        }

        //property implemented
        public DateTime InstatiateTime
        {
            get;
            set;
        }

        public string ServiceInstanceId
        {
            get;
            set;
        }

        public T GetService<T>()
        {
            if (this.instantiatedServices.ContainsKey(typeof(T)))
            {
                //found in earlier instantiated objects
                return (T)this.instantiatedServices[typeof(T)];
            }
            else
            {
                // lazy initialization
                try
                {
                    // use reflection to invoke the service
                    ConstructorInfo constructor = services[typeof(T)].GetConstructor(new Type[0]);

                    T service = (T)constructor.Invoke(null);

                    // add the service to the ones that we have already instantiated
                    instantiatedServices.Add(typeof(T), service);

                    return service;
                }
                catch (KeyNotFoundException)
                {
                    throw new ApplicationException("The requested service is not registered");
                }
            }
        }
    }
}

In this code I am creating IServiceLocator interface which has generic method

T GetService<T>();

Further I am adding to properties on the line of our entity interfaces.
1)  InstatiateTime
2)  ServiceInstanceId
That will save time at which instance of class implementing IServiceLocator created at and its instance Id.

There are three different implementation of interface IServiceLocator

1) ServiceLocator: Here constructor create repository of  available  services  method GetService provide available service. Drawback in this implementation is object of services created forehand even before somebody needs it.

2) LazyServiceLocator: Here also constructor create repository of available service type & their interface type. When First request for certain service come it creates the object of service and cache that object in on more repository which keeps map between service type & its object. Subsequent request to same service are serviced through cached object.This implementation takes care of drawback in earlier implementation i.e.  We are creating object of service whenever actual object is needed i.e. Lazy implementation.       
       
        Again what happens is if we end up with creating multiple instance of LazyServiceLocator class which but one instance is suffice to handle multiple call.

3) LazySingleTonServiceLocator: Here we improve our design further by making constructor of LazySingleTonServiceLocator private  & adding a public static method to get required instance

public static IServiceLocator GetServiceLocatorInstance

Inside this method calling private constructor two create instance of LazySingleTonServiceLocator and caching it in a static variable, on subsequent request we will not create a new instance of LazySingleTonServiceLocator object  but provide one that is already created.Locking is used so that multiple thread do not enter in object instantiation logic and creating multiple instance of LazySingleTonServiceLocator defeating purpose of making it singleton.

Inside program.cs add following code ,it demonstrate use of Service Locator classes

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace MyConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            #region NonLazyLocator
            Console.WriteLine(" ############### Simple Service Locator ###############");

            IServiceLocator Locator = new ServiceLocator();
            Console.WriteLine("\n Simple Service Locator Object created \n\n Instance Id:{0}:{0} \n Instatiated At {1}", Locator.ServiceInstanceId, Locator.InstatiateTime.ToLongTimeString());

            //sleep
            Console.WriteLine("\n Sleeping for moments for 20 secs...\n");
            System.Threading.Thread.Sleep(20000);

            //asking for service to add int
            //int
            Console.WriteLine("\n Looking for compatible service to add int...\n");
            IAddIntService LocatedService = Locator.GetService<IAddIntService>();
            int intOp = LocatedService.Add(25, 57);
            Console.WriteLine(" Service Instance to add int found...\n");
            Console.WriteLine(" Service Instance Id:{0}:{0} \n Instatiated At {1}", LocatedService.ServiceInstanceId, LocatedService.InstatiateTime.ToLongTimeString());

            //long
            Console.WriteLine("\n Looking for compatible service to add long...\n");
            IAddLongService LocatedServiceLong = Locator.GetService<IAddLongService>();
            long intOpLong = LocatedServiceLong.Add(25, 57);
            Console.WriteLine(" Service Instance to add long found...\n");
            Console.WriteLine(" Service Instance Id:{0}:{0} \n Instatiated At {1}", LocatedServiceLong.ServiceInstanceId, LocatedServiceLong.InstatiateTime.ToLongTimeString());

            //int again
            Console.WriteLine("\n Looking for compatible service to add int again...\n");
            IAddIntService LocatedServiceAgain = Locator.GetService<IAddIntService>();
            int intOpAgain = LocatedServiceAgain.Add(56, 35);
            Console.WriteLine(" Service Instance to add int found...\n");
            Console.WriteLine(" Service Instance Id:{0}:{0} \n Instatiated At {1}", LocatedServiceAgain.ServiceInstanceId, LocatedServiceAgain.InstatiateTime.ToLongTimeString());

            Console.WriteLine("\n Conclusion:Service Locator & Service Instances created at same time\n");
            Console.WriteLine(" ####################################################\n\n\n");
            #endregion

            #region LazyLocator
            Console.WriteLine("\n ############### Lazy Service Locator ###############");

            IServiceLocator LazyLocator = new LazyServiceLocator();
            Console.WriteLine("\n Lazy Service Locator Object created \n\n Instance Id:{0} \n Instatiated At :{1}\n", LazyLocator.ServiceInstanceId, LazyLocator.InstatiateTime.ToLongTimeString());

            //sleep
            Console.WriteLine("\n Sleeping for moments for 20 secs...\n");
            System.Threading.Thread.Sleep(20000);

            //asking for service to add int
            IAddIntService LocatedLazyIntService = LazyLocator.GetService<IAddIntService>();
            int intOpLazy = LocatedLazyIntService.Add(25, 57);
            Console.WriteLine("\n Looking for compatible service to add int ...\n");
            Console.WriteLine(" Service Instance to add int found...\n");
            Console.WriteLine(" Service Instance Id:{0} \n Instatiated At: {1}\n", LocatedLazyIntService.ServiceInstanceId, LocatedLazyIntService.InstatiateTime.ToLongTimeString());

            //asking for service to add long
            IAddLongService LocatedLazyLongService = LazyLocator.GetService<IAddLongService>();
            long longOpLazy = LocatedLazyLongService.Add(255, 457);
            Console.WriteLine("\n Looking for compatible service to long int ...\n");
            Console.WriteLine(" Service Instance to add long found...\n");
            Console.WriteLine(" Service Instance Id:{0} \n Instatiated At: {1}\n", LocatedLazyLongService.ServiceInstanceId, LocatedLazyLongService.InstatiateTime.ToLongTimeString());

            //asking again for service to add int again
            IAddIntService LocatedLazyIntServiceAgain = LazyLocator.GetService<IAddIntService>();
            int intOpLazyAgain = LocatedLazyIntServiceAgain.Add(253, 567);
            Console.WriteLine("\n Looking for compatible service to add int again...\n");
            Console.WriteLine(" Service Instance to add int found...\n");
            Console.WriteLine(" Service Instance Id:{0} \n Instatiated At: {1}\n", LocatedLazyIntServiceAgain.ServiceInstanceId, LocatedLazyIntServiceAgain.InstatiateTime.ToLongTimeString());

            Console.WriteLine("\n Conclusion:Service Locator & Service Instances created at different time (lazy loading...)\n");
            Console.WriteLine(" ####################################################");
            #endregion

            #region LazySingleTonLocator
            Console.WriteLine("\n\n\n ############### Lazy SingleTon Service Locator ###############");

            //pulling service locator object
            IServiceLocator LazySingleTonLocator = LazySingleTonServiceLocator.GetServiceLocatorInstance;
            Console.WriteLine("\n Lazy Singletome Service Locator Object created (once as SingleTon) \n\n Instance Id:{0} \n Instatiated At :{1}\n", LazySingleTonLocator.ServiceInstanceId, LazySingleTonLocator.InstatiateTime.ToLongTimeString());

   IAddIntService LocatedLazySingleTonIntService = LazySingleTonLocator.GetService<IAddIntService>();
            int intOpLazySingleTon = LocatedLazySingleTonIntService.Add(25, 57);

            //sleep
            Console.WriteLine("\n Sleeping for moments for 20 secs...\n");
            System.Threading.Thread.Sleep(20000);

            //again pulling Service Locator object
            IServiceLocator LazySingleTonLocatorAgain = LazySingleTonServiceLocator.GetServiceLocatorInstance;
            Console.WriteLine("\n Lazy Singletome Service Locator pulled again \n\n Instance Id:{0} \n Instatiated At :{1}\n", LazySingleTonLocator.ServiceInstanceId, LazySingleTonLocator.InstatiateTime.ToLongTimeString());

            Console.WriteLine("\n Conclusion:Lazy Singletome Service Locator instance is not created multiple time (SingleTon)");
            Console.WriteLine(" ####################################################");
            #endregion

            Console.ReadKey();
        }
    }
}


How to call our 1st Implementation?
 
IServiceLocator Locator = new ServiceLocator();
IAddIntService LocatedService = Locator.GetService<IAddIntService>();
int intOp = LocatedService.Add(25, 57);

How to call our 2nd Implementation?
 
IServiceLocator LazyLocator = new LazyServiceLocator();
IAddIntService LocatedLazyIntService = LazyLocator.GetService<IAddIntService>();
 int intOpLazy = LocatedLazyIntService.Add(25, 57);

How to call our 3rd  Implementation?
 
IServiceLocator LazySingleTonLocator = LazySingleTonServiceLocator.GetServiceLocatorInstance;
IAddIntService LocatedLazySingleTonIntService = LazySingleTonLocator.GetService<IAddIntService>();
           
int intOpLazySingleTon = LocatedLazySingleTonIntService.Add(25, 57);

Compile you console application and run, Here is my output
 
Output:
 
############### Simple Service Locator ###############
 Simple Service Locator Object created
 Instance Id:U1JGBWKB4deh5fj:U1JGBWKB4deh5fj
 Instatiated At 2:36:22 PM
 Sleeping for moments for 20 secs...
 Looking for compatible service to add int...
 Service Instance to add int found...
 Service Instance Id:Jr0gzKODGNs4K0n:Jr0gzKODGNs4K0n
 Instatiated At 2:36:22 PM
 Looking for compatible service to add long...
 Service Instance to add long found...
 Service Instance Id:d37g9tIkCQcmZvG:d37g9tIkCQcmZvG
 Instatiated At 2:36:22 PM
 Looking for compatible service to add int again...
 Service Instance to add int found...
 Service Instance Id:Jr0gzKODGNs4K0n:Jr0gzKODGNs4K0n
 Instatiated At 2:36:22 PM
 Conclusion:Service Locator & Service Instances created at same time #####################################################################
############### Lazy Service Locator ###############################
Lazy Service Locator Object created
Instance Id:QwADmquGl9p4JBm
Instatiated At :2:36:42 PM
Sleeping for moments for 20 secs...
Looking for compatible service to add int ...
Service Instance to add int found...
Service Instance Id:oI7FJx3mZWmCdAc
 Instatiated At: 2:37:02 PM
 Looking for compatible service to long int ...
Service Instance to add long found...
 Service Instance Id:UUz7Qkv7XjmvMOb
 Instatiated At: 2:37:02 PM
 Looking for compatible service to add int again...
 Service Instance to add int found...
 Service Instance Id:oI7FJx3mZWmCdAc
 Instatiated At: 2:37:02 PM
Conclusion:Service Locator & Service Instances created at different time (lazy loading...)
 ####################################################################
 ############### Lazy SingleTon Service Locator ####################
Lazy Singletome Service Locator Object created (once as SingleTon)
Instance Id:5pPqsOZB3NReL44
Instatiated At :2:37:02 PM
 Sleeping for moments for 20 secs...
 Lazy Singletome Service Locator pulled again
 Instance Id:5pPqsOZB3NReL44
 Instatiated At :2:37:02 PM
 Conclusion:Lazy Singletome Service Locator instance is not created multiple time (SingleTon)
 ####################################################
In console I am printing instance time & Instance Id of service locator object & service object whenever required to confirm that service instance are created lazily & service locator is not creating multiple time enforcing SingleTon implementation.


My code is adaptation of Stephan (see reference) but with service class that I can connect more easily.


References:

http://stefanoricciardi.com/2009/09/25/service-locator-pattern-in-csharpa-simple-example/

No comments:

Post a Comment