Source code for this POC at https://pocs.codeplex.com/SourceControl/latest#Service Locator Design Pattern/CalculatorApplication/
Have you ever use ASP.NET membership provider? All ASP.NET developers know what is the membership and what is the membership provider. They can easily use one of the providers shipped with .NET framework, they can use a provider shipped with a certain product or they can build their own provider. The behind scene for this functionality is a design pattern called Service Locator. Service Locator Design Pattern is a very useful design pattern to make your software extensible and maintainable. Actually there are many benefits push you to use this approach especially on the big systems as you can separate any part (Service) of your system and test it, re-implement it and deploy it without affect other running parts of the system because of the loosely-coupled relationship between Application and Service Implementation. We can summarize some its benefits as following:
Two main parts are the keys to understand the Service Locator pattern:
In this post we will walk-through to implement Calculator that any one can extend its functionality to support new mathematical operations
Source code for this post is available at https://pocs.codeplex.com
In the following steps we will define our service interface which is considered to be used as a contract between the service locator object and the implementation of the service. We will name it CalculatorOperationAPI as I supposed the result DLL will be ship with the application to be used to extend the functionality
using System; using System.Collections.Generic; namespace CalculatorOperationAPI { public interface IOperation { /// <summary> /// Return a list of the required paramters for current operation /// </summary> List<string> GetOperaionParameters(); /// <summary> /// Calculate method receive a dictionary with all parameter names and values /// Make the required operation and return the result as string /// </summary> string Calculate(Dictionary<string, string> parameters); } }
In this section we will write our calculator application code which represent our service locator mechanism that use the previous interface in all operations.
Note that:
- Our application doesn't know about the implementations of the different operations which it will provide to the user
- The application just reads all the registered operations and list them to user to be used
- The registration of operations are saved in static list object inside the code. However in a real case you have to read it from configuration file or database
Now follow the following steps to build service locator mechanism:
using CalculatorOperationAPI; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Remoting; using System.Text; namespace Calculator { class Program { static List<ServiceDetails> registeredOperations = new List<ServiceDetails>(); static void Main(string[] args) { //1- Register operations to the used by the application registeredOperations.Add(new ServiceDetails() {DisplayName = "Add", AssemblyName = "Operations", TypeName = "Operations.Add" }); registeredOperations.Add(new ServiceDetails() { DisplayName = "Subtract", AssemblyName = "Operations", TypeName = "Operations.Subtract" }); //2- Display all the registerd operation to the user to select the required operation for(int i = 0; i < registeredOperations.Count(); i ++) { ServiceDetails service = registeredOperations[i]; Console.WriteLine(string.Format("{0} : {1}",i,service.DisplayName)); } Console.WriteLine(""); Console.Write("Select Operation Number : "); int OperationNumber; string operation = Console.ReadLine(); Console.WriteLine(""); //3- Get the details of the selected operation from the registered services list ServiceDetails operationDetails = null; if (int.TryParse(operation, out OperationNumber) && OperationNumber <= registeredOperations.Count()) operationDetails = registeredOperations[OperationNumber]; if (operationDetails == null) { //if the user enter a wrong operation number (Not listed operation) Console.WriteLine("Operation Not Found"); } else { //4- Use the registered operation to calculate the value by initiate a new object on the runtime ObjectHandle obj = Activator.CreateInstance(operationDetails.AssemblyName, operationDetails.TypeName); //5- Cast this object to the IOperation interface IOperation operationImplementation = (obj.Unwrap()) as IOperation; //6- Use the interface to get list of required paramters for this application Dictionary<string, string> parameters = new Dictionary<string, string>(); //7- Ask the user to enter the paramters value List<string> listOfParamters = operationImplementation.GetOperaionParameters(); foreach (var paramName in listOfParamters) { Console.Write(string.Format("{0} : ",paramName)); parameters.Add(paramName, Console.ReadLine()); } //8- Use the interface to calculate the result and display it to the user Console.WriteLine(""); Console.WriteLine(string.Format("The result is : {0}",operationImplementation.Calculate(parameters))); } Console.WriteLine("Press any key to exit...."); Console.ReadKey(); } } class ServiceDetails { public string DisplayName { get; set; } public string Assembly { get; set; } public string TypeName { get; set; } } }
On the previous code we fist define a class called ServiceDetails that used to store one operation then we defined a global List<ServiceDetails> variable and named it registedOperations that will store all the registered operations on our application. In the main method we make the following steps that metioned on the code
In this step you register all different operations developed as a separated parts as we will see in the coming section. Registeration is made by add new operation (Service) and give it a name and define its assembly name and type name
In this step on our application we request the user to select one of the registered services by its display name. After the user select the service number we retrieve the operation (Service) data from registeredOperations list
In this step the application create a new instance from the operation type and cast it to IOperation interface then query its methods to calculate the operation and display the result to the user
Any one who have the CalculatorOperationAPI assembly can use it to implement IOpeartion interface as following:
using CalculatorOperationAPI; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Operations { public class Add:IOperation { public string Calculate(Dictionary<string, string> parameters) { int firstNumber = int.Parse(parameters["First Int Number"]); int secondNumber = int.Parse(parameters["Second Int Number"]); int result = firstNumber + secondNumber; return result.ToString(); } public List<string> GetOperaionParameters() { List<string> paramters = new List<string>(); paramters.Add("First Int Number"); paramters.Add("Second Int Number"); return paramters; } } }
After you finish the interface implementation all you need are:
On this prove of concept we add a reference to the Operations.dll to simplify the copy process
Here are the result: