Interfaces help decouple components, but still you'll face a problem:
C# doesn’t provide a built-in way to easily create objects that implement interfaces, except to create an instance of the concrete component with the new keyword.
You'll end up with code like this:
public class PasswordResetHelper {
public void ResetPassword() {
IEmailSender mySender = new MyEmailSender() ;
mySender.SendEmail();
}
}
This undermines goal of being able to replace MyEmailSender without having to change PasswordReset helper and thus no loosely coupled components.
Using Dependency Injection
There are two parts to the DI pattern.
1. Remove any dependencies on concrete classes from my component
2. Inject dependencies - Dependency Injection Container, also known as an IoC container
1). Removing Dependency:
By creating a class constructor that accepts implementations of the interfaces as arguments, like this:
public class PasswordResetHelper {
private IEmailSender emailSender;
public PasswordResetHelper(IEmailSender emailSenderParam) {
emailSender = emailSenderParam;
}
public void ResetPassword() {
emailSender.SendEmail();
}
}
Note: The PasswordResetHelper class declares its dependencies through its constructor. This is known as Constructor Injection.
You could also declare dependencies to be injected through a public property, known as Setter Injection.
· The constructor for the PasswordResetHelper class is now said to declare a dependency on the IEmailSender interface, meaning that it can’t be created and used unless it receives an object that implements the
· IEmailSender interface.
· In declaring its dependency, the PasswordResetHelper class no longer has any knowledge of MyEmailSender, it only depends on the IEmailSender interface.
· In short, the PassworsResetHelper no longer knows or cares how the IEmailSender interface is implemented.
2). Injecting Dependencies
You have resolved dependency issue, but how do you instantiate the concrete implementation of interfaces without creating dependencies somewhere else in the application?
As it stands, I still have to have statements somewhere in the application like these:
...
IEmailSender sender = new MyEmailSender();
helper = new PasswordResetHelper(sender);
...
To remove this level of dependency also you need to use a Dependency Injection Container, also known as an IoC container.
This is a component that acts as a broker between the dependencies that a class like PasswordResetHelper declares and the classes that can be used to resolve those dependencies, such as MyEmailSender.
The dependencies are injected into the PasswordResetHelper at runtime; that is to say, an instance of some class that implements the IEmailSender interface will be created and passed to the PasswordResetHelper constructor during instantiation. I.e. There is no compile-time dependency between PasswordResetHelper and any class that implements the interfaces it depends on.
Because the dependencies are dealt with at runtime, I can decide which interface implementations are going to be used when I run the application.
You can choose between different e-mail providers or inject a special mocked implementation for testing.
DI Behavior:
· Register the set of interfaces or abstract types that your application uses with the DI container, and specify which implementation classes should be instantiated to satisfy dependencies. I.e. Register IEmailSender interface with the container and specify that an instance of MyEmailSender should be created whenever an implementation of IEmailSender is required.
· Whenever you want a PasswordResetHelper object in my application, ask the DI container to create one for you. It knows that the PasswordResetHelper has declared a dependency on the IEmailSender interface and it knows that that you have specified that you want to use the MyEmailSender class as the implementation of that interface.
This way DI container puts these two pieces of information together, creates the MyEmailSender object and then uses it as an argument to create a PasswordResetHelper object, which I am then able to use in the application.
Note
It is important to note that you no longer create the objects in your application using the new keyword. Instead, you go to the DI container and request the objects you need.
Well you do not need to write your own DI container—there are some great open source and freely licensed implementations available. E.g. Ninject (www.ninject.org)
Ninject Clever Features:
The role of a DI container may seem simple and trivial, but that is not the case. A good DI container, such as Ninject, has some clever features:
1. Dependency Chain Resolution: If you request a component that has its own dependencies (e.g., constructor parameters), the container will satisfy those dependencies, too. So, if the constructor for the MyEmailSender class requires an implementation of the INetworkTransport interface, the DI container will instantiate the default implementation of that interface, pass it to the constructor of MyEmailSender and return the result as the default implementation of IEmailSender.
2. Object Lifecycle Management: If you request a component more than once, should you get the same instance each time or a fresh new instance?
A good DI container will let you configure the lifecycle of a component, allowing you to select from predefined options including:
· Singleton (the same instance each time),
· Transient (a new instance each time),
· Instance-Perthread,
· Instance-Per-Http-Request,
· Instance-From-A-Pool, and many others.
3. Configuration Of Constructor Parameter Values: If the constructor for my implementation of the INetworkTransport interface requires a string called serverName, for example, you should be able to set a value for it in your DI container configuration. It is a crude but simple configuration system that removes any need for your code to pass around connection strings, server addresses, and so forth.
Hope this helps.
Thanks & Regards,
Arun Manglick, (PMP, PMI-ACP, CSM, MS-Project, CSSGB, ITIL, MCPD, MCTS, MTECH)
Project Manager | http://www.synechron.com
No comments:
Post a Comment