So what's Ninject all about? First, let's examine the idea of dependency injection by walking through a simple example. Let's say you're writing the next blockbuster game, where noble warriors do battle for great glory. First, we'll need a weapon suitable for arming our warriors.

C#:
class Sword {
  public void Hit(string target) {
    Console.WriteLine("Chopped {0} clean in half", target);
  }
}

VB.NET:
Public Class Sword
  Public Sub Hit(ByVal target As String)
    Console.WriteLine("Chopped {0} clean in half", target)
  End Sub 
End Class

Then, let's create a class to represent our warriors themselves. In order to attack its foes, the warrior will need an Attack() method. When this method is called, it should use its Sword to strike its opponent.

C#:
class Samurai {
  private Sword _sword;
  public Samurai() {
    _sword = new Sword();
  }
  public void Attack(string target) {
    _sword.Hit(target);
  }
}

VB.NET:
Public Class Samurai
  Private _sword As Sword
  Public Sub New()
     _sword = New Sword() 
  End Sub
  Public Sub Attack(ByVal target As String)
     _sword.Hit(target) 
  End Sub 
End Class 

Now, we can create our Samurai and do battle!

C#:
class Program {
  public static void Main() {
    Samurai warrior = new Samurai();
    warrior.Attack("the evildoers");
  }
}

VB.NET:
Module Module1
  Sub Main()
    Dim warrior As New Samurai()
    warrior.Attack("the evildoers") 
  End Sub 
End Module 

As you might imagine, this will print Chopped the evildoers clean in half to the console. This works just fine, but what if we wanted to arm our Samurai with another weapon? Since the Sword is created inside the Samurai class's constructor, we have to modify the implementation of the class in order to make this change.

When a class is dependent on a concrete dependency, it is said to be tightly coupled to that class. In this example, the Samurai class is tightly coupled to the Sword class. When classes are tightly coupled, they cannot be interchanged without altering their implementation. In order to avoid tightly coupling classes, we can use interfaces to provide a level of indirection. Let's create an interface to represent a weapon in our game.

C#:
interface IWeapon {
  void Hit(string target);
}

VB.NET:
Public Interface IWeapon
  Sub Hit(ByVal target As String) 
End Interface

Then, our Sword class can implement this interface:

C#:
class Sword : IWeapon {
  public void Hit(string target) {
    Console.WriteLine("Chopped {0} clean in half", target);
  }
}

VB.NET:
Public Class Sword
  Implements IWeapon
  Public Sub Hit(ByVal target As String) Implements IWeapon.Hit
    Console.WriteLine("Chopped {0} clean in half", target)
  End Sub
End Class

And we can alter our Samurai class:

C#:
class Samurai {
  private IWeapon _weapon;
  public Samurai() {
    _weapon = new Sword();
  }
  public void Attack(string target) {
    _weapon.Hit(target);
  }
}

VB.NET:
Public Class Samurai
  Private ReadOnly _sword As IWeapon
  Public Sub New()
    _sword = New Sword()
  End Sub

  Public Sub Attack(ByVal target As String)
    _sword.Hit(target)
  End Sub
End Class

Now our Samurai can be armed with different weapons. But wait! The Sword is still created inside the Samurai's constructor. Since we still need to alter the implementation of Samurai in order to give our warrior another weapon, Samurai is still tightly coupled to Sword.

Fortunately, there is an easy solution. Rather than creating the Sword from within the Samurai's constructor, we can expose it as a parameter of the constructor instead.

C#:
class Samurai {
  private IWeapon _weapon;
  public Samurai(IWeapon weapon) {
    _weapon = weapon;
  }
  public void Attack(string target) {
    _weapon.Hit(target);
  }
}

VB.NET:
Public Class Samurai
  Private ReadOnly _sword As IWeapon
  Public Sub New(ByVal weapon As IWeapon)
    _sword = weapon
  End Sub

  Public Sub Attack(ByVal target As String)
    _sword.Hit(target)
  End Sub
End Class

Then, to arm our warrior, we can inject the Sword via the Samurai's constructor. This is an example of dependency injection (specifically, constructor injection). Let's create another weapon that our Samurai could use:

C#:
class Shuriken : IWeapon {
  public void Hit(string target) {
    Console.WriteLine("Pierced {0}'s armor", target);
  }
}

VB.NET:
Public Class Shuriken
  Implements IWeapon

  Public Sub Hit(ByVal target As String) Implements IWeapon.Hit
    Console.WriteLine("Pierced {0}'s armor", target)
  End Sub
End Class

Now, we can create an army of warriors:

C#:
class Program {
  public static void Main() {
    Samurai warrior1 = new Samurai(new Shuriken());
    Samurai warrior2 = new Samurai(new Sword());
    warrior1.Attack("the evildoers");
    warrior2.Attack("the evildoers");
  }
}

VB.NET:
Module Module1
  Sub Main()
    Dim warrior1 As New Samurai(New Shuriken())
    Dim warrior2 As New Samurai(New Sword())
    warrior1.Attack("the evildoers")
    warrior2.Attack("the evildoers") 
  End Sub 
End Module

This results in the following output to be printed to the console:

Pierced the evildoers armor.
Chopped the evildoers clean in half.


This is called dependency injection by hand, because each time you want to create a Samurai, you must first create some implementation of IWeapon and then pass it to the Samurai's constructor. Now that we can change the weapon the Samurai uses without having to modify its implementation, the Samurai class could be in a separate assembly from Sword - in fact, we can create new weapons without needing the source code of the Samurai class!

Dependency injection by hand is an effective strategy for small projects, but as your application grows in size and complexity, it becomes more and more cumbersome to wire all of your objects up. What happens when the dependencies have dependencies of their own? You can easily end up spending most of your time creating and wiring together objects, when you could be writing code that adds real value to your software. This is where dependency injectors like Ninject can help.

Continue reading: Dependency Injection With Ninject

Last edited Sep 13, 2009 at 2:11 AM by nkohari, version 5

Comments

DanAtkinson Feb 19, 2010 at 9:27 AM 
This is just me being anal, but the actual output of warrior1.Attack("the evildoers") is "Pierced the evildoers's armor.".