Understanding Inversion of Control, Dependency Injection and Service Locator

Understanding Inversion of Control, Dependency Injection and Service Locator

29 Mar 2024
Beginner
199K Views
7 min read
Learn via Video Course & by Doing Hands-on Labs

⭐ .NET Design Patterns Course: Design Patterns in C# Online Training

So many developers are confused about the term Dependency Injection (DI). The confusion is about terminology, purpose, and mechanics. Should it be called Dependency Injection, Inversion of Control, or Service Locator? Over the Internet, there are a lot of articles, presentations, and discussion but, unfortunately, many of them use conflicting terminology.

In this article, you will learn about all these three concepts. Let's start with dependency.

What is dependency?

Dependency can be understood with the help of a simple example. Suppose Class A needs Class B to do its job, Hence, Class B is a dependency of Class A.

Dependency Management

To make sure that class A will have a class B object, Class A use one the following option:

Among all of the above three options, the last one comes into IoC, since here Class A is not doing anything, it's receiving the object of Class B when it's required. So, this IoC since you are changing the normal flow of code execution.

Inversion of Control (IoC)

The term Inversion of Control (IoC) refers to a programming style where the flow of a program has been inverted i.e. changed from the normal way. As you have done in the example of Class A and Class B. Here, dependencies are instantiated by a framework or runtime and supplied to the desired class as needed.

Moreover, IoC is a generic term and it is not limited to DI. Actually, DI and Service Locator patterns are specialized versions of the IoC pattern or you can say DI and Service Locator are the ways of implementing IoC.

For example, Suppose your Client class needs to use a Service class component, then the best you can do is to make you Client class aware of an IService interface rather than a Service class. In this way, you can change the implementation of the Service class at any time (and for how many times you want) without breaking the host code.

IoC Implementation

IoC and DI

The terms Dependency Injection (DI) and Inversion of Control (IoC) are generally used as interchangeably to describe the same design pattern. The pattern was originally called IoC, but Martin Fowler (know for designing the enterprise software) proposed the name as DI because all frameworks or runtime invert the control in some way and he wanted to know which aspect of control was being inverted.

Dependency Injection (DI)

DI is a software design pattern that allows us to develop loosely coupled code. DI is a great way to reduce tight coupling between software components. DI also enables us to better manage future changes and other complexity in our software. The purpose of DI is to make code maintainable.

The Dependency Injection pattern uses a builder object to initialize objects and provide the required dependencies to the object means it allows you to "inject" a dependency from outside the class.

Service Locator (SL)

Service Locator is a software design pattern that also allows us to develop loosely coupled code. It implements the DIP principle and easier to use with an existing codebase as it makes the overall design looser without forcing changes to the public interface.

The Service Locator pattern introduces a locator object that objects are used to resolve dependencies means it allows you to "resolve" a dependency within a class.

Dependency Injection and Service Locator with Example

Let's consider the simple dependency between two classes as shown in the fig and it is a simple approach, that you know.

Now have a look at the following code:

public class Service
{
 public void Serve()
 {
 Console.WriteLine("Service Called");

 //To Do: Some Stuff
 }
}
 
public class Client
{
 private Service _service;
 
 public Client()
 {
 this._service = new Service();
 }
 
 public void Start()
 {
 Console.WriteLine("Service Started");
 this._service.Serve();

 //To Do: Some Stuff
 }
}

Clearly, the Client class has a dependency on the Service class. If you want to make it loosely coupled, you have to use IoC to make them more flexible and reusable it.

To implement the IoC, you have the choice of two main patterns: Service Locator and Dependency Injection. The Service Locator allows you to "resolve" a dependency within a class and the Dependency Injection allows you to "inject" a dependency from outside the class.

Using Service Locator

The above code can be re-written as by using Service Locator as follows.

public interface IService
{
 void Serve();
}

public class Service : IService
{
 public void Serve()
 {
 Console.WriteLine("Service Called");
 
 //To Do: Some Stuff
 }
}

public static class LocateService
{
 public static IService _Service { get; set; }

 public static IService GetService()
 {
 if (_Service == null)
 _Service = new Service();

 return _Service;
 }
}

public class Client
{
 private IService _service;

 public Client()
 {
 this._service = LocateService.GetService();
 }

 public void Start()
 {
 Console.WriteLine("Service Started");
 this._service.Serve();
 
 //To Do: Some Stuff
 }
}

Sample ServiceLocator-Implementation:

class Program
{
 static void Main(string[] args)
 {
 var client = new Client();
 client.Start();
 
 Console.ReadKey();
 }
}

The Inversion happens in the constructor, by locating the Service that implements the IService-Interface. The dependencies are assembled by a "Locator".

Using Dependency Injection

The above code can also be re-written as by using Dependency Injection as follows.

public interface IService
{
 void Serve();
}

public class Service : IService
{
 public void Serve()
 {
 Console.WriteLine("Service Called");

 //To Do: Some Stuff
 }
}

public class Client
{
 private IService _service;

 public Client(IService service)
 {
 this._service = service;
 }

 public void Start()
 {
 Console.WriteLine("Service Started");
 this._service.Serve();

 //To Do: Some Stuff
 }
}

Sample Builder-Implementation:

class Program
{
 static void Main(string[] args)
 {
 client = new Client(new Service());
 client.Start();

 Console.ReadKey();
 }
}

The Injection happens in the constructor, bypassing the Service that implements the IService-Interface. The dependencies are assembled by a "Builder" and Builder responsibilities are as follows:

  1. knowing the types of each IService

  2. according to the request, feed the abstract IService to the Client

What's wrong with the above two approaches?

Above two approaches can be resembled DI FACTORY. There are some issues with the above two approaches:

  1. It is hardcoded and can't be reused across applications, due to hardcoded factories. This makes the code stringent to particular implementations.

  2. It is Interface dependent since interfaces are used for decoupling the implementation and the object creation procedure.

  3. It's Instantiating instantiations are very much custom to a particular implementation.

  4. Everything is compiled time since all the dependent types for the objects in the instantiation process (factory) have to be known at compile time.

What is the Solution?

IoC containers are the solution to resolve the above two approaches issues. Hence, when we compose applications without a DI CONTAINER, it is like a POOR MAN’S DI. Moreover, DI CONTAINER is a useful, but optional tool.

You can also improve the above two approaches by doing some more work. You can also combine the above two approaches to make code more independent

Dependency Injection vs. Service Locator

  1. When you use a service locator, every class will have a dependency on your service locator. This is not the case with dependency injection. The dependency injector will typically be called only once at startup, to inject dependencies into the main class.

  2. The Service Locator pattern is easier to use in an existing codebase as it makes the overall design looser without forcing changes to the public interface. Code that is based on the Service Locator pattern is less readable than the equivalent code that is based on Dependency Injection.

Read More Articles Related to Dependency Injection
What do you think?

I hope you will enjoy the IoC, DI and SL patterns while writing the code. I would like to have feedback from my blog readers. Your valuable feedback, question, or comments about this article are always welcome.

Share Article
Batches Schedule
About Author
Shailendra Chauhan (Microsoft MVP, Founder & CEO at Scholarhat by DotNetTricks)

Shailendra Chauhan is the Founder and CEO at ScholarHat by DotNetTricks which is a brand when it comes to e-Learning. He provides training and consultation over an array of technologies like Cloud, .NET, Angular, React, Node, Microservices, Containers and Mobile Apps development. He has been awarded Microsoft MVP 8th time in a row (2016-2023). He has changed many lives with his writings and unique training programs. He has a number of most sought-after books to his name which has helped job aspirants in cracking tough interviews with ease.
Accept cookies & close this