Dev Patterns – Dependency Injection (aka Inversion of Control)

diamond-cubes-2-pattern-clip-art“Dependency Injection” (DI), also more cryptically known as “Inversion of Control” (IoC), can be used as a technique for encouraging this loose coupling.

John Munsch explains it like this:

When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn’t want you to have. You might even be looking for something we don’t even have or which has expired.

What you should be doing is stating a need, “I need something to drink with lunch,” and then we will make sure you have something when you sit down to eat.

In designing an object-oriented application, a major tenet of design is “loose coupling”. Objects should only have as many dependencies as is needed to do their job – and the dependencies should be few.

There are three primary approaches to implementing DI:

  • Constructor injection
  • Setter injection (also called Property injection)
  • Method injection

Constructor injection uses parameters to inject dependencies. In setter injection, you use setter methods to inject the object’s dependencies. Finally, in interface-based injection, you design an interface to inject dependencies.

Example of Tightly Bound Classes

In the following example, your Client class needs to use a Service class component. So it tightly binds with the Service class.


using System;
public class Service
{
public void Serve()
{
Console.WriteLine("Service Called");
//To Do: Some Stuff
}
}
public class Client
{
private Service _service;
public Client(Service service)
{
this._service = service;
}
public void Start()
{
Console.WriteLine("Service Started");
this._service.Serve();
//To Do: Some Stuff
}
}
class Program
{
static void Main(string[] args)
{
Client client = new Client(new Service());
client.Start();
Console.ReadKey();
}
}

But when it does that, you cannot use a different implementation of your Client class. The following will not compile.


class AlternateService
{
public void Serve()
{
Console.WriteLine("Service Called");
//To Do: Some Stuff
}
}
class TestClient
{
public static Main()
{
// will not compile. Cannot mock out the alternate service
Client client = new Client(new AlternateService());
client.Start();
Console.ReadKey();
}
}

This diagram shows how they are tightly bound. Arrows represent that the Program calls the Client and the Service directly.

image

Example of Constructor Injection

Constructor Injection is the most common DI.

Dependency Injection is done by supplying the DEPENDENCY through the class’s constructor when instantiating that class.

Injected component can be used anywhere within the class.

Should be used when the injected dependency is required for the class to function.

It addresses the most common scenario where a class requires one or more dependencies.

Sample of Construction Injection

In this case, your Client class needs to use a Service class component, you can make your 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.

For example:


using System;
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
}
}
class Program
{
static void Main(string[] args)
{
Client client = new Client(new Service());
client.Start();
Console.ReadKey();
}
}

The dependency diagram shows that the Client is no longer tightly bound to a particular service.

image

You can now use a Test Client with a different implementation of IService. In this case, you can call it AlternativeService.


class AlternateService :IService
{
public void Serve()
{
Console.WriteLine("Service Called");
//To Do: Some Stuff
}
}
class TestClient
{
public void Main(string[] args) {
// Will compile. Cannot mock out the alternate service
Client client = new Client(new AlternateService());
client.Start();
Console.ReadKey();
}
}

Either the test program or the client can choose the implementation of IService at runtime.

image

 

Property Injection

Property injection is also called Setter injection.

Used when a class has optional dependencies, or where the implementations may need to be swapped. Different logger implementations could be used this way.

May require checking for a provided implementation throughout the class(need to check for null before using it).

Does not require adding or modifying constructors

Property Injection Sample Code

The following is an example showing property injection.


using System;
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 IService Service
{
set
{
this._service = value;
}
}
public void Start()
{
Console.WriteLine("Service Started");
this._service.Serve();
//To Do: Some Stuff
}
}
class Program
{
static void Main(string[] args)
{
Client client = new Client();
client.Service = new Service();
client.Start();
Console.ReadKey();
}
}

Method Injection

Method injection injects the dependency into a single method, for use by that method.

Method injection can be useful where the whole class does not need the dependency, just the one method.

Method Injection Sample Code

Method injection is generally uncommon, usually used for edge cases. The following is a sample:


using System;
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 void Start(IService service)
{
this._service = service;
Console.WriteLine("Service Started");
this._service.Serve();
//To Do: Some Stuff
}
}
class Program
{
static void Main(string[] args)
{
Client client = new Client();
client.Start(new Service());
Console.ReadKey();
}
}

Summary

Here are some key take aways:

  • Reduces class coupling
  • Increases code reusing
  • Improves code maintainability
  • Improves application testing

Sample Code

Sample code for this post is available in the Patterns folder of the DevDays repository on GitHub.

Thanks

Special thanks to Shailendra Chauhan for the post on Implementation of Dependency Injection Pattern in C#.

References