Tuesday, February 14, 2012

Loosely coupled components, dependency injection (DI), and DI container

       During software development, we usually find different parts of our application are unnecessarily depended on each other. This reduces the flexibility of code and may cause issues. So we want the components of our application to be as independent as possible.

       Here is a simple example to describe how we can de-couple objects.For example, we need to build an application about a man drives car, truck, and tractor.We have a man class like this:

       public class Man
       {
              private Car car = new Car();
              private Truck truck = new Truck();
              private Tractor tractor = new Tractor();

              public Man() {}

              public void driveCar()
              {
                     car.drive();
              }

              public void driveTruck();
              {
                     truck.drive();
              }

              public void driveTractor();
              {
                     tractor.drive();
              }
       }

       As you can see, the "Man" class is coupled with 3 other classes. Any change on those classes (for example, Truck class is renamed to BigTruck) may cause Man class stop working or can not be compiled.

       It's time to de-couple them. We define an interface named IVehicle:

       public interface IVehicle
       {
              void drive();
       }

       We re-define Car, Truck, and Tractor class to implement this interface:

       public class Car : IVehicle
       {
              public void drive() { Console.WriteLine("drive car.");}
       }

       public class Truck : IVehicle
       {
              public void drive() {Console.WriteLine("drive truck.");}
       }

       public class Tractor : IVehicle
       {
              public void drive() {Console.WriteLine("drive tractor.");}
       }

       Then we change Man class as following to de-couple:

       public class Man
       {
              private IVehicle vehicle;

              public Man(IVehicle _vehicle)
              {
                     vehicle = _vehicle;
              }

              public void drive()
              {
                     vehicle.drive();
              }
       }

       We can "inject" the dependency at runtime like this:
    
       IVehicle vehicle = new Car();
       Man man = new Man(vehicle);
       man.drive(); // Man drives the car

       vehicle = new Truck();
       man = new Man(vehicle);
       man.drive(); // Man drives the truck

       vehicle = new Tractor();
       man = new Man(vehicle);
       man.drive(); // Man drives the tractor

       This is much better than the old code. But it's not enough, because we still have to create dependencies somewhere in our application. It's the time to introduce Dependency Injection Container, also known as IoC(Inversion of Control) container. It's an intermediate component to create the dependencies for us. What we need to do is register the interfaces with the DI container and tell it which actual class instances should be created to satisfy dependencies. In my next blog, I will talk about Ninject, a DI container can be added into Visual Studio 2010 project.

No comments: