While designing software systems, Architects press for loosely coupled software design. In this post let's see how to build loosely coupled class composition using dependency injection.
This blog post mainly aimed at explaining what is dependency injection with practical examples.
Code implemented without dependency injection
{
public Send(Message message)
{
//Send email using SmtpClient
}
}
You will use EmailManager class to send an email in your Client which probably looks something like below:
{
public void ProcessOrder(Order newOrder)
{
// Other business logic
// Send email
EmailManager mgnr = new EmailManager();
manager.Send(message);
}
}
Issues with code not using dependency injection
Above code will serve the purpose but there are two problems with code in the Client class.
- Cannot unit test ProcessOrder() method in the Client client.
- If requirements is changed from sending Email to text message, then you will have to modify Client class to use a new SMSManager instead of current EmailManager class. Changing an existing class to meet changing business requirement isn't always an option because of the regression involved. It violates OPEN for EXTENSION CLOSED for MODIFICATION design principle.
Refactor the code to eliminate design issues using Dependency injection
- CHANGE #1:Changes to EmailManager class. Let's create a interface 'INotificationManager' and declare Send() method, later make the EmailManager to implement the interface.
- CHANGE #2: Add a constructor to Client class to expect an object implementing INotificationManager interface
After the code refactoring the it will look like below:
{
void Send(Message message);
}
public class EmailManager:INotifictionManager
{
public void Send(Message message)
{
//logic to send email
}
}
public class Client
{
private INotificationManager _manager =null;
public Client(INotifictionManager manager)
{
_manager =manager;
}
public void ProcessOrder(Order newOrder){
{
//..other logic...and build Message to be sent
// send notification
_manager.send(message);
}
}
If you look at Client class in refactored code, its no longer creating an instance of EmailManager, that means it easy to unit the ProcessOrder() method using mocking.
Second, if in future business requirements changes from sending an email to sending a text message, then there will be no change in your client class. Instead of passing EmailManager instance, you will pass an instance of SmsTextManager class. That means there will be no change in your Client even if your requirement changes in future to send different type of notification.
If you have come so far reading the post and wondering where is the dependency injection, my answer is "You have already seen the dependency injection in the refactored code."
Finding what is dependency injection
The Client class need to send notification while processing an order. So The Client class has a dependency on the logic to send a notification. If you look at the refactored code, this logic is provided by INotifictionManager interface. So Client class has dependency on an instance of type INotifictionManager.
If you look carefully the Client class is not creating an instance of type INotifictionManager, instead of someone while instantiating Client is passing an instance of INotifictionManager type. So Client dependency on notification in being injected into it via INotifictionManager type. THIS IS CALLED DEPENDENCY INJECTION.
Very good example, simple explanation for dependency injection!
ReplyDeleteNice explanation
ReplyDelete