Introduction
Every day we write lots of code that are tightly coupled and when complexity grows code will eventually deteriorate into Spaghetti Code. Which violates the Dependency inversion principle. In software design, tight coupling is often considered to be a liability in design. When one class knows explicitly about the design and implementation of another class, it raises the risk that changes to one class will break the other class.
On the other hand loosely coupled code can stay maintainable and well-designed. The benefits of using loose coupling aren’t always instantly evident, but they become visible over time, as the complexity of a code grows. Dependency Injection Pattern is nothing but the best way to enable loose coupling. In this article, I will try to explain how we can use DI in our everyday practice with a simple approach without DI containers.
Background
Uncle Bob “SOLID” object-oriented design principles “D” stands for Dependency Inversion Principle.
The Dependency Inversion Principle states:
- High-level modules should not depend upon low-level modules. Both should depend upon abstractions.
- Abstractions should not depend upon details. Details should depend upon abstractions.
Sometimes its not easy to maintain DIP while writing your code. Practice and experience will help you out from this. The Dependency Inversion principle (DIP) helps to loosely couple your code by ensuring that your high-level modules depend on abstractions rather than concrete implementations of lower-level modules. The Dependency Injection pattern is an application/ implementation of this principle.
The basic idea of this article is that how we can make our code loosely coupled.
Using the code
Let’s start from the code. Many of us probably seen (or written) code like this:
public class Email
{
public void SendEmail()
{
// code
}
}
public class Notification
{
private Email _email;
public Notification()
{
_email = new Email();
}
public void PromotionalNotification()
{
_email.SendEmail();
}
}
Here Notification
class has a dependency on Email
class. In this case, the notification creates an instance of the e-mail directly inside of the notifications constructor and knows exactly what kind of email class it’s creating and consuming. It’s violate DIP.
A class has dependency on some other class and knows a lot about the other classes it interacts with is said to be tightly coupled. When a class knows explicitly about the design and implementation of another class, its raise the risk that changes to one class will break the other class.
What if we want to send some other types of notifications like SMS or save into the DB? To enable this behavior we have to modify the implementation of the notification class.
To reduce dependency we have to do a couple of steps. Firstly introduce an abstraction layer between these two classes. We can use interface/ abstract class to represent the abstractions between Notification and Email.
public interface IMessageService
{
void SendMessage();
}
public class Email : IMessageService
{
public void SendMessage()
{
// code
}
}
public class Notification
{
private IMessageService _iMessageService;
public Notification()
{
_iMessageService = new Email();
}
public void PromotionalNotification()
{
_iMessageService.SendMessage();
}
}
Here, introduced an interface IMessageService
to represent the abstraction, and ensure that the Notification
class only calls methods or properties on that interface.
Secondly need to move the creation of the Email
class outside of the Notification
. We can achieve this by DI pattern.
DI is the act of supplying all classes that a service needs rather that leaving the responsibility to the service to obtain dependent classes. DI typically comes in three flavors:
- Constructor Injection
- Property Injection
- Method Injection
Using these injections we can achieve the second step. I am applying these injections in our tightly coupled code. And making our code loosely coupled and maintaining DIP.
Constructor Injection
This is the most common dependency injection. When a class requires an instance of a DEPENDENCY to work, we can supply that DEPENDENCY through the class’s constructor. Now let’s change the Notification class to support constructor injection:
public class Notification
{
private IMessageService _iMessageService;
public Notification(IMessageService _messageService)
{
this._iMessageService = _messageService;
}
public void PromotionalNotification()
{
_iMessageService.SendMessage();
}
}
In this code, there are several benefits: the implementation of the constructor is very simple, it has reduced the number of things Notification needs to know about, any code that wants to create an instance of Notification can look at the constructor and know exactly what kinds of things are necessary to make Notification function. So, our code is now loosely coupled and easily maintainable.
Property Injection
Property injection/ setter injection is less common dependency injection, it is best used when the dependency is optional. We have to expose a writable property that allows a client to supply a different implementation of the class’s DEPENDENCY than the default. We have to change our Notification class to apply property injection:
public class Notification
{
public IMessageService MessageService
{
get;
set;
}
public void PromotionalNotification()
{
if (MessageService == null)
{
// some error message
}
else
{
MessageService.SendMessage();
}
}
}
We have removed the constructor and replaced it with a property. Now we are providing dependency via properties rather than the constructor. In the PromotionalNotifications
method we have to check that it has already provided the service dependency or not by checking MessageService
value. Here, we able to made our code loosely coupled.
Method Injection
When a DEPENDENCY can vary with each method call, you can supply it via a method parameter. Suppose our application will send Email also SMS or save notification message into DB. We can use method injection. We can write the code following way:
public class Email : IMessageService
{
public void SendMessage()
{
// code for the mail send
}
}
public class SMS : IMessageService
{
public void SendMessage()
{
// code for the sms send
}
}
public class Notification
{
public void PromotionalNotification(IMessageService _messageService)
{
_messageService.SendMessage();
}
}
Conclusion
Surprisingly it’s very easy to write tightly coupled code! One of the many reasons is that while introducing new/ additional features in our code. Every time we use a new keyword we introduce a tight coupling. We can minimize this by introducing constructor injection. In general, developers like to use constructor injection over the other two types of injection. But of course, we can mix both of them, while constructor will be mandatory and the other two will be optional. Hope this article will help you to begin DI in your everyday code.