In this Article I am going to discuss about "Dependency injection" on which I am working and exploring. In this article I am going to discuss about what is dependency injection and why there is need of this software design pattern.
Dependency injection
To understand dependency injection better way, let’s consider below code,
Image above shows the representation of creation and use of the objects.
Problem with code is
Definition of this pattern is: Remove hard coded dependency and make it possible to replace at run-time or compile time.
In following section I am going to describe how problem listed above get resolved with the Dependency injection pattern.
Solution
1)Make client i.e. first class in dependency graph which consume service to create all dependent object
Resolution to the first problem of code above, pass the responsibility of supplying all required dependency to the first in dependency graph i.e. client class which is first class of the dependency graph.
So the code will change like this
2)Removes tight coupling between object i.e allow to define loose coupling
To understand the second problem better way, let’s consider with the real life scenario of Computer or laptop.
As you can see in the above image we have the port for each device external device to which I can associate external device and do our work.
But problem with this is I cannot attach my keybord on printer port and vice versa, same problem occurs with the other devices. So this is like tight coupling that I cannot change my external device on the give interface i.e. on which I am depends.
Solution to this is USB port.
If I have USB port than I can easily attach any device to my machine and perform my task.
What is tight coupling between classes? (In programming)
Let start with simple example:
Now as you see in above code class NeedDependencyClass utilized class DependencyClass, this means that class NeedDependencyClass is depend on class DependencyClass. This type of dependency is tight dependency between class NeedDependencyClass and class DependencyClass. Because class DependencyClass is passed as parameter to class NeedDependencyClass and class NeedDependencyClass utilizing method of class DependencyClass. Now if the why it called as tightly coupled. If code is like this to create
now if I created class DependencyClass1 like as follows public
and I want to pass DependencyClass1 instead of DependencyClass in class and want to call test method
This will give compiler error because I am trying to pass DependencyClass1 object as parameter. So this means Class NeedDependencyClass cannot accept any another class than Class DependencyClass.
Solution to this problem is define and interface as below which get implemented by classes
So the code become like this
And this code will work both the class DependencyClass and DependencyClass1
Image below shows DependencyClass object creation only done by client class object and others class down the line use supplied dependent object. And class NeedDependencyClass is depends on the interface IDependencyClass, which can be implemented and replicable by any new class DependencyClass.
3) Make your class depend on Abstraction
Now there is requirement like only authenticated user of the application can run Test method in the application, than developer of the class need to modify the Test method like this.
That’s violate SOLID principle “Open Close” which says "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".
So to avoid it same as make use of the abstraction i.e. create interface as which already done for to resolve tight coupling, so class extension can be done easily.
Below is how to extend the code that doesn’t break “Open Close” principle.
As you see in above code new class is created with the name SecureDependencyClass. This class extends the functionality of the previous class and also not breaks “Open Close” principle.
This is done by injecting exiting class DependencyClass in the new class SecureDependencyClass i.e that is also example of Dependency injection. So SecureDependencyClass consume dependency DependencyClass.
And which change consumer client code like as below
Image below shows SecureDependencyClass object creation only done by client class object and others class down the line use supplied dependent object. And class NeedDependencyClass is depends on the interface IDependencyClass, which can be implemented and replicable by any new class SecureDependencyClass.
This also shows the NeedDependencyClass is not dependent on specific class but on the Abstraction.
4) Make class Testable
Now once the second step class NeedDependencyClass easily becomes testable. Because problem with original code is hardcode dependency that is not replaceable.
Example , following is the final code
As per the code Test method of the class DependencyClass does connection with sql server and return value.
But for testing of the class NeedDependencyClass there is no need to perform expensive database connection. So we replace class DependencyClass with the TestDependencyClass class like as below.
Conclusion
So Dependency injection is one of important design pattern which allows developing maintainable code which also satisfies SOLID principals. DI not only helpful in above this problem but we can also achieve below, Other thing can be achievable by DI is
Dependency injection
To understand dependency injection better way, let’s consider below code,
Public class client { Public static void main() { NeedDependencyClass a = new NeedDependencyClass(); } } public class NeedDependencyClass { Private readonly DependencyClass _b; public NeedDependencyClass () { _b = new DependencyClass(); } public void Test() { _b.Test(); } } public class DependencyClass { public void Test() { // code for text method } }In above code class client creates NeedDependencyClass and use it, then class NeedDependencyClass consume service of class DependencyClass to perform task i.e. class NeedDependencyClass is depend on class DependencyClass to perform task. This kind of the code is hardcoded dependency.
Image above shows the representation of creation and use of the objects.
Problem with code is
- Class NeedDependencyClass itself responsible for creating its own dependency.
- Code is tightly coupled, because dependency cannot be replaceable.
- Code also violate “Open Closed” rule of SOLID principal which says that "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification". Because do changes in the Test method I need to modify DependencyClass.
- Testing of Class NeedDependencyClass becomes difficult as I cannot replace Dependency class DependencyClass with other dependency.
Definition of this pattern is: Remove hard coded dependency and make it possible to replace at run-time or compile time.
In following section I am going to describe how problem listed above get resolved with the Dependency injection pattern.
Solution
1)Make client i.e. first class in dependency graph which consume service to create all dependent object
Resolution to the first problem of code above, pass the responsibility of supplying all required dependency to the first in dependency graph i.e. client class which is first class of the dependency graph.
So the code will change like this
Public class client { Public static void main() { NeedDependencyClass a = new NeedDependencyClass(new DependencyClass()); } } public class NeedDependencyClass { Private readonly DependencyClass _b; public NeedDependencyClass(DependencyClass b) { _b = b; } public void Test() { _b.Test(); } } public class DependencyClass { public void Test() { // code for text method } }Image below shows object creation only done by client class object and others class down the line use supplied defendant object.
2)Removes tight coupling between object i.e allow to define loose coupling
To understand the second problem better way, let’s consider with the real life scenario of Computer or laptop.
As you can see in the above image we have the port for each device external device to which I can associate external device and do our work.
But problem with this is I cannot attach my keybord on printer port and vice versa, same problem occurs with the other devices. So this is like tight coupling that I cannot change my external device on the give interface i.e. on which I am depends.
Solution to this is USB port.
If I have USB port than I can easily attach any device to my machine and perform my task.
What is tight coupling between classes? (In programming)
Let start with simple example:
public class NeedDependencyClass { Private readonly DependencyClass _b; public NeedDependencyClass(DependencyClass b) { _b = b; } public void Test() { _b.Test(); } } public class DependencyClass { public void Test() { // code for text method } }
Now as you see in above code class NeedDependencyClass utilized class DependencyClass, this means that class NeedDependencyClass is depend on class DependencyClass. This type of dependency is tight dependency between class NeedDependencyClass and class DependencyClass. Because class DependencyClass is passed as parameter to class NeedDependencyClass and class NeedDependencyClass utilizing method of class DependencyClass. Now if the why it called as tightly coupled. If code is like this to create
NeedDependencyClass a = new NeedDependencyClass(new DependencyClass());
now if I created class DependencyClass1 like as follows public
class DependencyClass1 { public void Test() { //code for text method } }
and I want to pass DependencyClass1 instead of DependencyClass in class and want to call test method
NeedDependencyClass a = new NeedDependencyClass(new DependencyClass1());
This will give compiler error because I am trying to pass DependencyClass1 object as parameter. So this means Class NeedDependencyClass cannot accept any another class than Class DependencyClass.
Solution to this problem is define and interface as below which get implemented by classes
Interface IDependencyClass { Void Test(); } Class DependencyClass : IDependencyClass { …… } Class DependencyClass1 : IDependencyClass { …… }
So the code become like this
public class NeedDependencyClass { Private readonly IDependencyClass _b; public NeedDependencyClass(IDependencyClass b) { _b = b; } public void Test() { _b.Test(); } }
And this code will work both the class DependencyClass and DependencyClass1
Image below shows DependencyClass object creation only done by client class object and others class down the line use supplied dependent object. And class NeedDependencyClass is depends on the interface IDependencyClass, which can be implemented and replicable by any new class DependencyClass.
3) Make your class depend on Abstraction
Now there is requirement like only authenticated user of the application can run Test method in the application, than developer of the class need to modify the Test method like this.
public class DependencyClass : IDependencyClass { public void Test() { using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "YOURDOMAIN")) { bool isValid = pc.ValidateCredentials("myuser", "mypassword"); if(isValid) Console.Writeln(“Testing method for the data”); } } }
That’s violate SOLID principle “Open Close” which says "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".
So to avoid it same as make use of the abstraction i.e. create interface as which already done for to resolve tight coupling, so class extension can be done easily.
Below is how to extend the code that doesn’t break “Open Close” principle.
Class DependencyClass : IDependencyClass { public void Test() { Console.Writeln(“Testing method for the data”); } } Class SecureDependencyClass : IDependencyClass { IDependencyClass _b; public SecureDependencyClass(IDependencyClass b) { _b = b; } public void Test() { using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "YOURDOMAIN")) { bool isValid = pc.ValidateCredentials("myuser", "mypassword"); if(isValid) this._b.Test(); } } }
As you see in above code new class is created with the name SecureDependencyClass. This class extends the functionality of the previous class and also not breaks “Open Close” principle.
This is done by injecting exiting class DependencyClass in the new class SecureDependencyClass i.e that is also example of Dependency injection. So SecureDependencyClass consume dependency DependencyClass.
And which change consumer client code like as below
Public class client { Public static void main() { IDependencyClass dependency = new SecureDependencyClass(new DependencyClass()); NeedDependencyClass a = new NeedDependencyClass(dependency); } } public class NeedDependencyClass { Private readonly IDependencyClass _b; public NeedDependencyClass(IDependencyClass b) { _b = b; } public void Test() { _b.Test(); } }
Image below shows SecureDependencyClass object creation only done by client class object and others class down the line use supplied dependent object. And class NeedDependencyClass is depends on the interface IDependencyClass, which can be implemented and replicable by any new class SecureDependencyClass.
This also shows the NeedDependencyClass is not dependent on specific class but on the Abstraction.
4) Make class Testable
Now once the second step class NeedDependencyClass easily becomes testable. Because problem with original code is hardcode dependency that is not replaceable.
Example , following is the final code
public class NeedDependencyClass { Private readonly IB _b; public NeedDependencyClass(IB b) { _b = b; } public int Test() { var c=_b.Test(); return c*10; } } public class DependencyClass :IDependencyClass { public void Test() { // code for getting value form database using(SqlConnection c = new SqlConnection(connectionstring)) { c.open(); SqlDataReader myReader = null; SqlCommand myCommand = new SqlCommand("select count(*) from table", c); return (Int32) cmd.ExecuteScalar(); } }
As per the code Test method of the class DependencyClass does connection with sql server and return value.
But for testing of the class NeedDependencyClass there is no need to perform expensive database connection. So we replace class DependencyClass with the TestDependencyClass class like as below.
public class NeedDependencyClass { Private readonly IDependencyClass _b; public NeedDependencyClass(IDependencyClass b) { _b = b; } public int Test() { var c=_b.Test(); return c*10; } } public class TestDependencyClass :IDependencyClass { public void Test() { return 10; } } Public class TestClient { Public static void main() { NeedDependencyClass a = new NeedDependencyClass(new TestDependencyClass()); } }
Conclusion
So Dependency injection is one of important design pattern which allows developing maintainable code which also satisfies SOLID principals. DI not only helpful in above this problem but we can also achieve below, Other thing can be achievable by DI is
- Parallel programming of application
- Late Binding
No comments:
Post a Comment