Saturday, May 10, 2014

What is Dependency Injection? And why it is so important?

Definition found on WIKI
Dependency injection is a software design pattern that implements inversion of control and allows a program design to follow the dependency inversion principle. The term was coined by Martin Fowler.
An injection is the passing of a dependency (a service) to a dependent object (a client). The service is made part of the client's state. Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.

Means: The dependency should be injected to a object which it needs to complete their unit of work. If object A needs object B to complete its unit of work, then B should be provided to A instead of A creating object B. This also means abstraction should not depend on details but instead details should depend on abstractions.

Let's drill down into this and try to make more sense out of this with examples.
NOTE: All my examples will be using C# language

Lets assume, in my application i need to send updates like email to user when a particular task is complete. So based on the requirement, i wrote a class which sends email messages to the users.
public class EmailNotifier
{
  public void Send(string message)
  {
   // code to send email to the user
  }
}

So in my code, i will be using email notifier to send email messages on complete of a task to respective users.

public class Task 
{
  // other methods for the task class

  public void OnComplete(string message)
  {
   // notify user with message
   var notifier = new EmailNotifier();
   notifier.Send(message);
  }
}

Now this is all good but we have couple of concerns over here.

Issue: Testing
Since my code has dependency on the external class, we have to make sure that its dependencies are always available, up and running for testing. Plus we cannot do unit testing of our unit of work as we will be testing notifier class and its dependencies too.
Solution:
Instead of creating the instance of the notifier class inside task object, we can pass the object as an parameter in the class. This is also known as Dependency Injection. Doing so, our code will not require to know how to build notifier object, and for testing we can pass the mocked object(s) which simulates different behavior to capture different scenarios.

public class Task 
 {
  EmailNotifier _emailNotifier;

  public Task(EmailNotifier emailNotifier)
  {
   _emailNotifier = emailNotifier;
  }

  // other methods for the task class

  public void OnComplete(string message)
  {
   // notify user with message
   _emailNotifier.Send(message);
  }
 }

With this solution, task class gets the notifier object injected as parameter. Task class is no longer responsible for creating the notifier object.

This is good but it can be still improved by making this loosely coupled. What exactly this means? To understand this, first we need to understand tight coupling.
Tight Coupling:
In computer science, tight coupling (or tightly coupled) is a type of coupling that describes a system in which hardware and software are not only linked together, but are also dependent upon each other. One of the common characteristics of tight coupling are
  • A change in one module usually forces a ripple effect of changes in other modules.
  • Assembly of modules might require more effort and/or time due to the increased inter-module dependency.
  • A particular module might be harder to reuse and/or test because dependent modules must be included.
Loose Coupling:
In computing and systems design, a loosely coupled system is one in which each of its components has, or makes use of, little or no knowledge of the definitions of other separate components.
In the above code, even though we are injecting dependencies, still it is tightly coupled because it directly depends on concrete object. Any new requirement will cause ripple effect of changes e.g. if business comes up with new requirement of sending user notification via SMS, twitter, etc. To accommodate the new requirement, we can either change the base class to implement new notifications but that change will effect the way updates are sent where ever its being used for this service, which might not be the requirement. In another solution, we can inject all of the notification objects (like email, SMS etc) in the task class. Problem with this approach is that we will be creating instance of all notifiers even though we will be using few of them depending on requirement, we will clutter our code with if else statements and this is memory heavy too. It will work but again, its not the most desirable way of implementing a solution. Anytime new mode of notification is required, we have to change our code, including business logic.

Better solution would be to pass an abstraction/interface as a dependency. We can create a interface which describe how a Notifier class should look like. Then we will implement different ways of notifiers against this contract like this

 public interface INotifier
 {
  void Send(string message);
 }

 public class EmailNotifier : INotifier
 {
  public void Send(string message)
  {
   // code to send email to the user
  }
 }

 public class SmsNotifier : INotifier
 {
  public void Send(string message)
  {
   // code to send sms to the user
  }
 }

 public class EmailAndSmsNotifier : INotifier
 {
  public void Send(string message)
  {
   // code to send email and sms to the user
  }
 }

Now, instead of injecting concrete class, we can inject interface as dependency which promises methods or properties to be present in any class which implements it. This is how new task class will look like

public class Task 
 {
  INotifier _notifier;

  public Task(INotifier notifier)
  {
   _notifier = notifier;
  }

  // other methods for the task class

  public void OnComplete(string message)
  {
   // notify user with message
   _notifier.Send(message);
  }
 }

With new changes we can inject the different types of notifiers user want. In future, any new business need of notifications can been implemented without any major change in the code. We will need to create new class implementing the interface, test it as unit of work and inject it as dependency.
Now the bigger question is, how we inject these dependencies ...
One way is to create factory classes for resolving out dependencies but in any scenario, we will clutter our code with tight coupling.
Other solution is to use DI APIs. They are handy in registering and resolving interface/class types. Registration can been done at the application start. We can update the framework to use our DI object to resolve types in code.

Stayed tuned to my next article to learn more about how to use Unity, DI API in .net applications.

Till then, Happy coding ...

No comments:

Post a Comment