Design patterns are one thing that none of us can escape.  If you work in the software development industry and as a part of a team, then odds are that you have run into design patterns. 

When you boil it down, a design pattern is a approach to coding that solves a specific problem.  These patterns are great because they make it possible for other developers to immediately see our code and have some idea of the underlying architecture to it.

Today I'm going to be talking about a newer design pattern, one that I have been using a lot recently and offers several advantages to help decouple your code and build it in a way that separates layers and uses interfaces to remove dependencies...the service locator pattern.

 

What is a Service Locator Pattern Anyway?

A Service Locator pattern essentially when you boil it down is like a pharmacist.  Think about this, when you go to the pharmacist you take a prescription from the doctor of what you need, and you give it to the pharmacist, and based on that script they will give you back the drug on the script, or something that fits the same bill. 

A service locator utilizes an interface in the same way.  You tell it an interface that your interested in, and it returns a concrete class that implements that interface.

Below is an example of a service locator class, this class is usually implemented as a singleton, and serves as the only way that a concrete class can be called.
1: public interface IServiceLocator 2: { 3: T GetService(); 4: } 5:  6: public class ServiceLocator : IServiceLocator 7: { 8: private IDictionary services; 9:  10: public ServiceLocator() 11: { 12: services = new Dictionary(); 13:  14: //Add Interface to class references here for repositories 15: 16: } 17:  18: public T GetService() 19: { 20: try 21: { 22: return (T)services[typeof(T)]; 23: } 24: catch (KeyNotFoundException) 25: { 26: throw new ApplicationException("The requested service is not registered"); 27: } 28: } 29: }

Specifically, a service locator operates on the principle of Inversion of Control.  Which is a development technique that over the past few years has gained a lot of traction.  You could do a whole article series on inversion of control, but can find a great article here.

For our purposes, Inversion of Control refers to the concept that instead of the class making the call deciding what it needs, it instead relies on a different construct to provide the concrete implementations of interfaces.

The key to the ServiceLocator class is that it maintains a dictionary listing of the interfaces and the matching classes that implement that interface.  This dictionary is loaded at line 14 above. 

 

Here’s an example:

Now, all of this is well and good but let’s look at some examples.  Let’s say we have an application that handles grading for a school.  And in that application we have the following code.

1: public interface IGrade 2: { 3: int PointsPossible { get; set; } 4: int PointsEarned { get; set; } 5: decimal Percentage { get; set; } 6: string LetterGrade { get; set; } 7: void CalculateGrade(); 8: string GradeType { get; set; } 9: } 10:  11: public class Assignment : IGrade 12: { 13: public int PointsPossible { get; set; } 14: public int PointsEarned { get; set; } 15: public decimal Percentage { get; set; } 16: public string LetterGrade { get; set; } 17: public string GradeType { get; set; } 18:  19: public Assignment() 20: { 21: GradeType = "Assignment"; 22: } 23:  24: public void CalculateGrade() 25: { 26: Percentage = PointsEarned / PointsPossible; 27: if (Percentage >= 90) 28: { 29: LetterGrade = "A"; 30: } 31: else if (Percentage >= 80) 32: { 33: LetterGrade = "B"; 34: } 35: else if (Percentage >= 70) 36: { 37: LetterGrade = "C"; 38: } 39: else if (Percentage >= 60) 40: { 41: LetterGrade = "D"; 42: } 43: else 44: { 45: LetterGrade = "F"; 46: } 47: } 48: }

The IGrade interface, provides an generic version of what ever grade is made up of.  The Assignment class, implements those methods, in addition to providing functionality specific to an exam. 

In our service locator, the class would look as follows:

1: public interface IServiceLocator 2: { 3: T GetService(); 4: } 5:  6: public class ServiceLocator : IServiceLocator 7: { 8: private IDictionary services; 9:  10: public ServiceLocator() 11: { 12: services = new Dictionary(); 13:  14: //Add Interface to class references here for repositories 15: services.Add(typeof(IGrade), new Assignment()); 16: } 17:  18: public T GetService() 19: { 20: try 21: { 22: return (T)services[typeof(T)]; 23: } 24: catch (KeyNotFoundException) 25: { 26: throw new ApplicationException("The requested service is not registered"); 27: } 28: } 29: }

Notice that we added a single line showing the interface, adding to the dictionary an instance of the Interface, with the value being a new instance of the class. 

Now, let’s  say we have a page that allows us to add assignments, that save method would look like this.

1: public void AddGrade(int pointsPossible, int pointsEarned) 2: { 3: IServiceLocator locator = new ServiceLocator(); 4: IGrade grade = locator.GetService(); 5:  6: grade.PointsEarned = pointsEarned; 7: grade.PointsPossible = pointsPossible; 8: grade.CalculateGrade(); 9: }

Notice that our method makes no reference to the assignment class, or anything outside of the interface that it needs.  This allows us flexibility in that if I want to replace assignment, with Exam, I can do so within the service locator, and have it carry through the entire application. 

Why Do We Care?

The benefit of using this approach is that it makes your code more flexible and allows you to swap out old classes for new as requirements change.  This allows you to update your application in a way that minimizes the impact to the rest of the application.

Additionally, this can help with testability, because now using a Fakes Assembly, or just a different Service Locator, I can swap out my application classes for testing classes in my test project.

Service Locator is a valuable tool when applied appropriately and should be consider as a way to reduce dependencies and coupling in your applications.