Step by step Dependency Injection tutorial with Ninject

Dependency Injection is one of the assets in software engineering allowing to developer reduce tight coupling between entities. But first allow me to share some code. It would be easy to introduce concepts based on some examples.

Let’s assume we need to write code which according to selected type of communication should send confirmation data to the customer. At this point you able to communicate with user by  Email only. Now, for introduction purposes, i will use KISS approach and in context of this tutorial it will be small and stand alone console application.

Great, you say 🙂 So our code might be looking pretty straightforward:

public interface IConfirmationMessageSender
{
   void Send(string message, string recipient);
}
public class EmailMessageSender: IConfirmationMessageSender
{   
   public void Send(string message, string recipient)
   { 
     Console.WriteLine("Email recipient={0} : data={1}", recipient,message);
   }
}
public class MessageSender
{ 
   // a lot of complicated stuff here :)
   // ..
   // ..
  
   public void SendMessage(string message, string recipient)
   {
     EmailSender emailSender = new EmailSender(); 
     emailSender.Send(message,recipient); 
   } 

   // ..
   // ..

}

Lets create call it and see results:

class Program
{   
   void Main(string[] args)
   {
      MessageSender messageSender = new MessageSender();
      messageSender.SendMessage("Some text","123456789"); 
   }
}

After we run the code, our results will be look as following:

Email recipient=123456789 : data=Some text

Looks great and clean code (kind’a 🙂 ), but it suffers mainly of  3 large issues: extension, tight coupling and testability.

Why extension is an issue:

In the future you will receive additional demand regarding user’s communication type: besides Email it already supporting, you should add SMS support. In order to achieve that you have to change you architecture which directly violate one of the SOLID principles. Any another demand will break your current architecture.

Why tight coupling issue:

Lets take a look in MessageSender class. There you actually create dependency between MessageSender and EmailSender.

Why testability here is an issue :

The problem here – you unable to test MessageSender alone, without EmailSender. Its’ conceptually wrong since the main idea of unit testing is to divide functionality to small chunks and test them as standalone entities whenever its possible. In addition, in the future you will change EmailSender itself or replace it with some other entity – your unit testing will be failed.

Solution:

This is the place where Dependency Injection comes in! The purpose of  DP is to reduce hard-coded dependencies between classes.

Lets reconsider our architecture. In addition we should not forget additional demand (communication by SMS):

public interface IConfirmationMessageSender
{
   void Send(string message, string recipient);
}
public class EmailMessageSender: IConfirmationMessageSender
{   
   public void Send(string message, string recipient)
   { 
     Console.WriteLine("Email recipient={0} : data={1}", recipient,message);
   }
}
public class SMSMessageSender: IConfirmationMessageSender
{   
   public void Send(string message, string recipient)
   { 
     Console.WriteLine("SMS recipient={0} : data={1}", recipient,message);
   }
}
public class MessageSender
{
 IConfirmationMessageSender _messageSender = null;

 public MessageSender(IConfirmationMessageSender messageSender)
 {
   _messageSender = messageSender;
 }

 public void SendMessage(string message, string recipient) 
 { 
   _messageSender.Send(message,recipient); 
 } 
}

Lets create call it and see results:

class Program
{   
   void Main(string[] args)
   {
      IConfirmationMessageSender confirmationMessage = new SMSMessageSender();
      MessageSender messageSender = new MessageSender(confirmationMessage); 
      messageSender.SendMessage("Some text","123456789");
      Console.ReadKey(); 
   } 
}

After we run the code, our results will be look as following:

SMS recipient=123456789 : data=Some text

At this point:

  • Extension: Now any programmer able to add new communication types without breaking things (in simple words) and it’s not violate open-close principle (in smarter words) anymore.
  • Testability: We able to test any entity alone i.e. MessageSender can be tested with any communication type now and in a future
  • Tight coupling: There is no Tight coupling anymore between MessageSender and EmailMessageSender class.

 

Ninject Framework:

There a couple of framework might be using in order to achieve Inversion Of Control: Unity , Ninject Windsor and etc. Since i recently experimenting with Ninject,  it’s a good place to show some examples.

Ninject has nice features, but we will talk here about the basics. At this point all we have to do is change some code in our Main method. Everything else doesn’t changes.

Define couple usings:

using Ninject;
using Ninject.Modules;

Now we going to define Bindings class (you can call it whatever you want) and it have to extend NinjectModule class:

public class Bindings: NinjectModule
{   
   public override void Load()
   { 
     Bind<IConfirmationMessageSender>().To<SMSMessageSender>(); 
   } 
}

The idea here to define to Ninject how to threat each interface.

Then we will define kernel entity and load mapping from interface to implementation. In this case it makes to load Bindings class: in another case it might be loading from XML:

IKernel kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());

Actually kernel is a main point which make to all magic happens 🙂

And now we going to get implementation from kernel:

IConfirmationMessageSender confirmationMessage = new kernel.Get<IConfirmationMessageSender>(); 
MessageSender messageSender = new MessageSender(confirmationMessage); 
messageSender.SendMessage("Some text","123456789");

 

The final code:

using Ninject;
using Ninject.Modules;
public interface IConfirmationMessageSender
{
   void Send(string message, string recipient);
}
public class EmailMessageSender: IConfirmationMessageSender
{   
   public void Send(string message, string recipient)
   { 
     Console.WriteLine("Email recipient={0} : data={1}", recipient,message);
   }
}
public class SMSMessageSender: IConfirmationMessageSender
{   
   public void Send(string message, string recipient)
   { 
     Console.WriteLine("SMS recipient={0} : data={1}", recipient,message);
   }
}
public class MessageSender
{
 IConfirmationMessageSender _messageSender = null;

 public MessageSender(IConfirmationMessageSender messageSender)
 {
   _messageSender = messageSender;
 }

 public void SendMessage(string message, string recipient) 
 { 
   _messageSender.Send(message,recipient); 
 } 
}

public class Bindings: NinjectModule
{   
   public override void Load()
   { 
     Bind<IConfirmationMessageSender>().To<SMSMessageSender>(); 
   } 
}
class Program
{   
   void Main(string[] args)
   {
      IKernel kernel = new StandardKernel();
      kernel.Load(Assembly.GetExecutingAssembly());
      IConfirmationMessageSender confirmationMessage = kernel.Get<IConfirmationMessageSender>(); 
      MessageSender messageSender = new MessageSender(confirmationMessage); 
      messageSender.SendMessage("Some text","123456789");
      Console.ReadKey();
   } 
}

Download from here 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *