Dependency Injection

From Logic Wiki
Jump to: navigation, search


Basically, instead of having your objects creating a dependency or asking a factory object to make one for them, you pass the needed dependencies in to the constructor or via property setters, and you make it somebody else's problem (an object further up the dependency graph, or a dependency injector that builds the dependency graph). A dependency as I'm using it here is any other object the current object needs to hold a reference to.

One of the major advantages of dependency injection is that it can make testing lots easier. Suppose you have an object which in its constructor does something like:

public SomeClass() {
    myObject = Factory.getObject();
}

This can be troublesome when all you want to do is run some unit tests on SomeClass, especially if myObject is something that does complex disk or network access. So now you're looking at mocking myObject but also somehow intercepting the factory call. Hard. Instead, pass the object in as an argument to the constructor. Now you've moved the problem elsewhere, but testing can become lots easier. Just make a dummy myObject and pass that in. The constructor would now look a bit like:

public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

Advantages

  • Because dependency injection doesn't require any change in code behavior it can be applied to legacy code as a refactoring. The result is clients that are more independent and that are easier to unit test in isolation using stubs or mock objects that simulate other objects not under test. This ease of testing is often the first benefit noticed when using dependency injection.
  • Dependency injection allows a client to remove all knowledge of a concrete implementation that it needs to use. This helps isolate the client from the impact of design changes and defects. It promotes reusability, testability and maintainability.
  • Dependency injection can be used to externalize a system's configuration details into configuration files allowing the system to be reconfigured without recompilation. Separate configurations can be written for different situations that require different implementations of components. This includes, but is not limited to, testing.
  • Reduction of boilerplate code in the application objects since all work to initialize or set up dependencies is handled by a provider component.
  • Dependency injection allows concurrent or independent development. Two developers can independently develop classes that use each other, while only needing to know the interface the classes will communicate through. Plugins are often developed by third party shops that never even talk to the developers who created the product that uses the plugins.

Disadvantages

  • Dependency injection can make code difficult to trace (read) because it separates behavior from construction. This means developers must refer to more files to follow how a system performs.[citation needed]
  • Dependency injection typically requires more lines of code to accomplish the same behavior legacy code would.[citation needed]
  • Dependency injection diminishes encapsulation by requiring users of a system to know how it works and not merely what it does.
  • Dependency injection increases coupling by requiring the user of a subsystem to provide for the needs of that subsystem.

Example

You have two classes like this

public class EldestSon 
{
 public void Help(string message)
 {
    //do something 
 }
}

public class YoungestSon 
{
 public void Help(string message)
 {
    //do something 
 }
}

When we want to use them in Dad class

public class Dad
{
 EldestSon eldestson = null;
 YoungestSon youngestson = null;
 
 public void Notify(string message)
 {
   youngestSon = youngestSon?? new YoungestSon();
   eldestSon = eldestSon?? new EldestSon();
   //some logic to select which one to use 
   eldestSon.Help(message)
 }
}

But this is not a good way. Creating all instances first and use them in a class is weird and it's close to new sons

instead we introduce an interface ISon

public interface ISon
{
  void Help(string message);
}

and use it in Dad class

public class Dad
{
  ISon son = null;
  public void Notify(string message)
  {
    if(son==null)
    {
      // Now we gotta fix which so dad wanna call ?
    }
    // Do something here to select the son 
    son.Help(message);
}

So son classes should be now like

public class EldestSon : ISon
{
 public void Help(string message)
 {
    //do something 
 }
}

public class YoungestSon : ISon
{
 public void Help(string message)
 {
    //do something 
 }
}

A Dad's gotta pick his son

  1. A dad can pick a son based on condition
  2. A dad can have a preferable son in a situation
  3. Someone else can advise dad which son is better to call now.

Types of Dependency Injection

  1. Constructor Injection
  2. Method Injection
  3. Property Injection

Constructor Injection

First we introduce a constructor in Dad's class

public class Dad
{
  ISon son = null;

  public Dad(ISon selectedSon)
  {
    son = selectedSon;
  }
  public void Notify(string message)
  {
    son.Help(message);
  }
}

Selection can be made now when we call Dad

class Program
{
  static void Main(string[] args)
  { 
     EldestSon son = new EldestSon();
      Dad dad = new Dad(son);
      dad.Notify("Hello");
  }
}

Method Injection

public class Dad
{
  ISon son = null;

  public void Notify(ISon selectedSon, string message)
  {
    son.Help(message);
  }
}

Selection can be made now when we call Dad

class Program
{
  static void Main(string[] args)
  { 
     EldestSon son = new EldestSon();
      Dad dad = new Dad();
      dad.Notify(son, "Hello");
  }
}

If it's just the help you want then Method Injection could be used

Other Example

First create a simple 3 layer project design.

namespace CustomerUI
{
  //UI
  class Program 
  {
    static void Main(string[] args)
    {
      Customer obj = new Customer();
      obj.CustomerName = "aa";
       obj.Add();
    }
  }

  // Middle Layer
  public class Customer
  {
    private SQLServerDAL Odal = new SQLServerDal();
    public string CustomerName {get; set;}
    public void Add()
    {
      Odal.Add();
    }
  }

  // DAL
  public class SQLServerDAL
  {
    public void Add()
    { 
        console.write("Added");
    }
  }

}

if Oracle comes in

namespace CustomerUI
{
  //UI
  class Program 
  {
    static void Main(string[] args)
    {
      Customer obj = new Customer();
      obj.CustomerName = "aa";
       obj.Add();
    }
  }

  // Middle Layer
  public class Customer
  {
    private OracleDal OdalOra = new OracleDal();
    private SQLServerDAL Odal = new SQLServerDal();
    public string CustomerName {get; set;}
    public void Add()
    {
      if (true)   // can be config setting or anything else
      {
        Odal.Add();    // SQL Server
      }else
      {
        OdalOra.Add();  // Odarcle
      }
    }
  }

  // DAL
  public class SQLServerDAL
  {
    public void Add()
    { 
        console.write("Added");
    }
  }
  public class OracleDAL
  {
    public void Add()
    { 
        console.write("Added");
    }
  }
}

First thing we should do is creating interfaces

namespace CustomerUI
{
  //UI
  class Program 
  {
    static void Main(string[] args)
    {
      Customer obj = new Customer();
      obj.CustomerName = "aa";
       obj.Add();
    }
  }

  // Middle Layer
  public class Customer
  {
    private OracleDal OdalOra = new OracleDal();
    private SQLServerDAL Odal = new SQLServerDal();
    public string CustomerName {get; set;}
    public void Add()
    {
      if (true)   // can be config setting or anything else
      {
        Odal.Add();    // SQL Server
      }else
      {
        OdalOra.Add();  // Odarcle
      }
    }
  }

  // DAL
    public interface IDal
    {
     void Add();
    }
  public class SQLServerDAL : IDal
  {
    public void Add()
    { 
        console.write("Added");
    }
  }
  public class OracleDAL : IDal
  {
    public void Add()
    { 
        console.write("Added");
    }
  }
}

Now we need to change instansiation of database

      if (true)   // can be config setting or anything else
      {
        Odal = new SQLServerDAL();
      }else
      {
        Odal = new OracleDAL();
      }
      Odal.Add(); 

it's still tighlty coupled arthicecture let's do some more

public class Customer
{
   private IDal Odal;
   public string CustomerName {get; set;}
   // write a constructor
   public Customer(IDal iObj)
   {
      Odal = iObj;
   }
   public void Add

      Odal.Add(); 
}

OK now invoke Customer object like this

 Customer obj = new Customer(new SQLServerDAL());

We have to move that to an other framework to make it DI

So we may use UNITY.

Download it by Nuget

using Microsoft.Practices.Unity;

Now create a container and register classes

 class Program 
  {
    static void Main(string[] args)
    {
      IUnityContainer objContainer = new UnityContainer();
      objContainer.RegisterType<Customer>();   // register cutomer class and let unity be aware that I have a customer class
      objContainer.RegisterType<IDal, SQLServerDAL>();
      objContainer.RegisterType<IDal, OracleDAL>();

This can go to global.asax or any other part of application.

now get the object

 Customer obj =  objContainer.resolve<Customer>();

Real Life Sample Steps

  1. Create a class Data Project
  2. Create an Interface Domain Project
  3. Add a line of injection to Ioc Project
  4. if it's a static method use this
var emailService = IocConfig.Setup().GetInstance<IEmail>();
manager.EmailService = emailService;

if it's a normal method

public class EmailService 
{
   private readonly IEmail _email;
   public EmailService(IEmail email)
     {
       _email = email;
     }
   public Task SendAsync(IdentityMessage message)
   {
     _email.Send(message.Subject, message.Body, message.Destination);
     return Task.FromResult(0);
   }

}