What are these Acronyms?
IoC | Inversion of Control |
DIP | Dependency Inversion Principle |
DI | Dependency Injection |
In order to understand these, let’s take a look at the key terms.
Key Terms:
Dependency Inversion Principle
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.
- Agile Principles, Patterns, and Practices in C#
Benefits from DIP?
- Software Architecture such as DDD, Domain Centric Driven Design, SOLID OOP Design and Design Patterns use this concept to leverage requirements of changing technologies.
- Allow our application to use different kind of algorithms, data store, etc;
- Multiple implementation instead of chain inheritance;
- Easier to test by injecting fakes or Moqs.
These are the patterns that implementing DIP.
Uses of Inversion of Control:
- It decouples dependencies between high-level and low-level layers through shared abstractions.
- Acts as an interface between two system or components.
- It controls over the flow of an application
- It controls the dependency creation and bindings
Kinds of Inversion of Controls
- Interface Inversion
- Flow Inversion
- Dependency Creation/Binding Inversion
Most of the time when we implement DIP we use interface. This is a good design. When we expect that high-level modules will be depending on several dependencies, we should invert these dependencies by adding another layer of abstraction using interface. In these manner we could easily swap these dependencies.
Eg: In Domain Driven Design, the Core Domain is the brain of the application. The Data Layer, UI Layer, Sub Domains, Presentation Layer, and Test will be dependent on the Core Domain. This is typical DIP example in which we avoided the Business Layer to be dependent on the Data Layer. By doing this, we can swap any database provider we want even in-memory database collections. We can also extend our application to be expose on Web, desktop, WCF, or Web Services. We can also test our application by injecting in-memory fake data.
During the DOS mode era under programming 101, we used procedural UI interface (eg. Enter your Name[name], enter key, Enter your Course[course] ). The user must press the enter key to show the next entry and this is a problem. Flow Inversion allow the user to navigate through the field of entries before finally saving it.
Dependency Creation is another way of implementing DIP. We can invert the control by creating objects outside the class they are being used in. Meaning, objects are already created before injecting to the class that uses it. The advantage of using this approach is that we easily swap any object we want to inject.
Let's focus out attention to Dependency Creation and see how to implement them. There are lots of patterns that implement these concept but we will discussing the commonly used patterns.
- Dependency Inversion
- Constructor Injection
- Setter Injection/Property Injection
- Interface Injection
- Factory Pattern, Service Locator Pattern, and other patterns
This is the most popular way of implementing DIP. When we think about DIP we think about DI.
3 ways to Implement DI
3 ways to Implement DI
This is the most common DI where dependencies are pass to dependent via constructor.
Eg: In the example below, we have a class that can use different kind of encryption algorithm. So we want to inject that algorithm the moment we call the Encrypt class.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Encrypt { | |
private readonly IEncrtyptor Encryptor; | |
//The dependency is passed through the constructor | |
public Encrypt(IEncrtyptor encryptor) | |
{ | |
this.Encryptor = encryptor; | |
} | |
public string EncryptString(string myString) | |
{ | |
return Encryptor.Encrtypt(myString); | |
} | |
public string DecryptString(string myString) | |
{ | |
return Encryptor.Decrypt(myString); | |
} | |
} |
To inject the dependency, we create the object and pass on calling class.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//The dependency is injected as soon as the object is created. | |
Encrypt myEncryptor = new Encrypt(new HashEncrypt()); | |
string Encrypted = myEncryptor.EncryptString("myPassword9883"); | |
Console.WriteLine(Encrypted); |
To implement Setter Injection, use the properties to hold the dependencies. When using Setter, make the property public.
Eg:
To inject the dependency, we must set the object to the property.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class EncryptInvertionUsingSetter | |
{ | |
//The public property that will be use for injecting dependency | |
public IEncrtyptor Encryptor {get; set;} | |
public string EncryptString(string myString) | |
{ | |
return Encryptor.Encrtypt(myString); | |
} | |
public string DecryptString(string myString) | |
{ | |
return Encryptor.Decrypt(myString); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
EncryptInvertionUsingSetter EncryptUseSetter = new EncryptInvertionUsingSetter(); | |
//The next line does the Setter Injection | |
EncryptUseSetter.Encryptor = new HashEncrypt(); | |
//Setter Injection ends here. | |
string myPassEncrypted = EncryptUseSetter.EncryptString("myPassword9883"); | |
Console.WriteLine(myPassEncrypted); |
Another way to implement Inversion is through Interface Injection. Looking at the code, it sounds like a Method Injection.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace InterfaceInjection | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
IEncrtyptor dependency = new HashEncrypt(); | |
EncryptInvertionUsingInterface EncryptUseInterface = new EncryptInvertionUsingInterface(); | |
//This is where we inject the dependency | |
((IDependencyInterface)EncryptUseInterface).Inject(dependency); | |
Console.WriteLine(EncryptUseInterface.EncryptWithSalt("ddsdddsd")); | |
} | |
} | |
//This class implements the Interface injection | |
public class EncryptInvertionUsingInterface : IDependencyInterface | |
{ | |
private IEncrtyptor Encryptor; | |
//This is method that handles dependency | |
public void Inject(IEncrtyptor encryptor) | |
{ | |
this.Encryptor = encryptor; | |
} | |
public string EncryptWithSalt(string string2Encrypt) | |
{ | |
return Encryptor.Encrypt("12" + string2Encrypt); | |
} | |
} | |
public interface IDependencyInterface | |
{ | |
void Inject(IEncrtyptor encryptor); | |
} | |
public interface IEncrtyptor | |
{ | |
string Encrypt(string string2Encrypt); | |
string Decrypt(string string2Decrypt); | |
} | |
public class HashEncrypt : IEncrtyptor | |
{ | |
public string Encrypt(string string2Encrypt) | |
{ | |
return string2Encrypt; | |
} | |
public string Decrypt(string string2Decrypt) | |
{ | |
return string2Decrypt; | |
} | |
} | |
} |
Factory Pattern implements Inversion by adding an abstraction layer as a central place for creating objects. If we try to look at other patterns it uses Inversion Creation patterns to abstract and handle dependencies.
So what are these Castle Windsor, Unity, Structure Map, and Ninject. Well, they are Dependency Resolvers or IoC Containers. Their responsibilities is to handle dependencies for us. Most especially when dependencies grow in numbers.
You can build your own IoC Containers just like the Factory Pattern but these APIs are handy and make the job easier for you.
I hope I was able to share knowledge and Thank you for your time.
You can build your own IoC Containers just like the Factory Pattern but these APIs are handy and make the job easier for you.
I hope I was able to share knowledge and Thank you for your time.
1 comments:
Great information
Replydata science training in bangalore
powershell training in bangalore
gst training in bangalore
web designing training in bangalore
Machine Learning training in bangalore