Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια

C# and .NET Tips and Tricks

Quests in programming in .NET
Inversion of Control (IoC) and Dependency Injection (DI) and why they are important.

In this blog post, we are going to see how and when  Dependency Injection (DI) and Inversion of Control(IoC) can be applied for writing nice and maintainable code. Note that although there are plenty of tools out there that give you the ability to apply DI out of the box, in this post we are going to implement such a tool on our own gaining valuable insight on the workings of IoC and DI.

 

Suppose that in your code you have two classes (A,B) like the following:

public class A
{
public A(){}
public void Method1(){ }
public void Method2() { }
}

public class B
{
private A _a;
public B()
{
_a = new A();
_a.Method1();
_a.Method2();
}
}

 

That is, we have a class A that has two methods and class B that has a field of type A which is initialized and used in the constructor. Whenever we have such situation we say that “B depends on A” since B needs to create and use A directly. Also, B “has the control” of the creation of A. We will show that dependency graphically with an arrow as follows:

 

image

 

If you implement a new class A1 with the same functionality as A and want to use A1 in B, then you would need to change in B the type of field _a to A1 and its instantiation and recompile the project. If you use A only in one class in your program and your program is a single .exe file (no .dlls) then the merits of IoC cannot be shown and in my opinion you may introduce an unnecessary overhead if you apply it. But let’s see how this changes if the previous condition does not apply.

 

Suppose now that your app is a single .exe file but you have more than one class using A, say B,C,D. Each one of those classes (B,C,D) have a field of type A and use it in their code (B,C,D depend on A).

 

image

 

Switching now from A to A1 requires you to go to “each and every one” of the classes that use A (in a big project this also means that you have to remember which classes are those-in our example B,C,D) and make the appropriate changes. As you must have realized, such process, is error prone and takes time. This problem is created because B,C,D depend on A and B,C,D have the control of creating instances of A.

 

When you use Inversion of Control in the previous situation (changing A to A1) you only need to change a single line of code. So lets see how this works:

 

  1. We create an interface (IA) that has the methods of A. A and A1 should implement this interface.
  2. All classes that use (depend on) A now should use (depend on) IA.
  3. We either need to pass an instance that implement IA in the constructor of those classes or we need to implement a third class Creator that creates objects that implement IA and B,C,D should use this class to get instances that implement IA.

 

If we pass instances that implement IA in the constructors the code looks like the following:

interface IA {
void Method1();
}

public class A:IA {
public A(){}
public void Method1(){ }
}

public class B {
private IA _a;
public B(IA AImplementation) {
_a = AImplementation;
_a.Method1();
}
}

 

In the initial implementation “B had the control of creating A and B depended on A”. In this implementation “the user of B has the control of creating A and B does not depend on A but on IA”. This is Inversion of Control. Now if C,D also follow the same implementation changing from A to A1 does not affect them at all as long as you pass to their constructors and object of type A1 instead of an object of type A. Therefore the previous issue of having to change B,C,D to switch from A to A1 has been solved by using Ioc.

 

Of course, a lot of you would argue that you still have to change the instantiation of A to A1 to each and every one of the classes that create B,C,D to pass the correct parameter in the constructor. For this reason you can use a creator class (a factory) that returns objects that implement IA and use this to create your classes.

 

If we use a creator class and not pass the IA object in the constructor, the code looks like the following:

interface IA {
void Method1();
}

public class A:IA {
public A(){}
public void Method1(){ }
}

public static class Creator {
public A GetIA() {return new A();}
}

public class B {
private IA _a;
public B() {
_a = Creator.GetIA();
_a.Method1();
}
}

See that now _a in B is of type IA and B asks the Creator to get an instance of A. A implements IA. C,D classes would also use IA and the Creator to get an instance that implements A. A1 would need to implement IA also and switching from A to A1 would only require changing the method GetIA in the creator to return a new instance of A1 instead of A. This “Creator” class is usually called a “factory”.

 

Inversion of Control has enabled such a change to be fast, efficient and error free. You can change from A to A1 by changing one line and you can use a new A2 easily by making it implement IA. The drawback is that the amount of code we have written has tripled.

 

Inversion of Control has also another very important benefit in the case where your project uses a .dll. Imagine that you have your ASP.NET MVC project using a class library project which is your business layer. Then, your final deployment consists of the web app and the library dll. If your class A is part of your library and B is part of your web app and you do not use IoC, each time you change from A to A1 you need to recompile the whole project. On the contrary if you use IoC and a factory, the interface IA resides in the library the creator class (factory) resides also in the library and your B class in the web app uses IA. Therefore changing from A to A1 requires you to recompile only the library (.dll) and not all the project facilitating easy and fast deployment in changes.

 

Now wouldn’t  it be nice if we didn’t have to recompile even the library when changing from A to A1? Just have a way to do that by just changing a setting? Can we inject that dependency (A,A1) somehow without having to recompile? This is what we call “Dependency Injection”. How would we implement this without the help of any other libraries?

 

We could keep in a file the class type that needs to be instantiated for the interface and have our “Creator” class (factory) read this file, find out the name of the class that needs to be instantiated and create it by reflection. This would inject the required class in the dependency and thus the term “Dependency Injection”. This means that with dependency injection you do not have to recompile your app when switching from A to A1. The implementation of the creator class for IA should follow the steps below (full code in the project you can download at the end of the post):

 

  1. Open the file with the pairs (interface to implementation).
  2. Find the class name to instantiate
  3. Use reflection to instantiate class from the assembly
  4. Return new object implementing the interface

 

And the file that holds the pair can contain entries as follows:

 

DIandIOCDemo.IA=DIandIOCDemo.A

 

So when should you use Inversion of Control and Dependency Injection?

 

The general rule of thumb is to use it when you have many classes using the methods of a single one and this one may have more than one different “versions”. For example, you are creating your DB Access repositories and you also need to have some fake ones for your tests. Or you have to versions of a class one with lots of debugging info and one without and want to switch between the two at a deployed application.

 

Note that there are fully blown libraries that handle dependency injection such as spring.netthat you should also check out.

 

The project can be downloaded here

.

Shout it

Share
Posted: Πέμπτη, 9 Ιουνίου 2011 7:45 μμ από το μέλος iwannis
Δημοσίευση στην κατηγορία: ,
Ποιά είναι η άποψή σας για την παραπάνω δημοσίευση;

(απαιτούμενο)

(απαιτούμενο)

(προαιρετικό)

(απαιτούμενο)
ÅéóÜãåôå ôïí êùäéêü:
CAPTCHA Image

Ενημέρωση για Σχόλια

Αν θα θέλατε να λαμβάνετε ένα e-mail όταν γίνονται ανανεώσεις στο περιεχόμενο αυτής της δημοσίευσης, παρακαλούμε γίνετε συνδρομητής εδώ

Παραμείνετε ενήμεροι στα τελευταία σχόλια με την χρήση του αγαπημένου σας RSS Aggregator και συνδρομή στη Τροφοδοσία RSS με σχόλια