C
C#3mo ago
textYash

Purpose of Dependency Injection

In this example what's the benefit of DI here. I can just create instances of EmailService and MessagingService class and call the SendMessage method on them without having to create an extra instance of the NotificationService class.
namespace basic_programs{
interface IMessagingService {
string SendMessage(string message);
}
class EmailService : IMessagingService {
public string SendMessage(string message) {
return $"Email sent: {message}";
}
}
class SMSService : IMessagingService {
public string SendMessage(string message) {
return $"SMS sent: {message}";
}
}
class NotificationService(IMessagingService messaging_service)
{
private readonly IMessagingService _messagingService = messaging_service;

public string SendNotification(string message) {
return _messagingService.SendMessage(message);
}
}
class Lab23 {
public static void NotificationDemo() {
NotificationService email_service = new(new EmailService());
NotificationService sms_service = new(new SMSService());
System.Console.WriteLine(email_service.SendNotification("Hello"));
System.Console.WriteLine(sms_service.SendNotification("Hello"));
}
}
}
namespace basic_programs{
interface IMessagingService {
string SendMessage(string message);
}
class EmailService : IMessagingService {
public string SendMessage(string message) {
return $"Email sent: {message}";
}
}
class SMSService : IMessagingService {
public string SendMessage(string message) {
return $"SMS sent: {message}";
}
}
class NotificationService(IMessagingService messaging_service)
{
private readonly IMessagingService _messagingService = messaging_service;

public string SendNotification(string message) {
return _messagingService.SendMessage(message);
}
}
class Lab23 {
public static void NotificationDemo() {
NotificationService email_service = new(new EmailService());
NotificationService sms_service = new(new SMSService());
System.Console.WriteLine(email_service.SendNotification("Hello"));
System.Console.WriteLine(sms_service.SendNotification("Hello"));
}
}
}
I asked AI then it just mentioned: 1. some theoretical information about how DI has loose coupling and all but couldn't prove any benefit in the code. 2. how I can independently test the NotificationService but I can just independently test MessagingService & EmailService. 3. how adding features to the MessagingService & EmailService can cause problems but couldn't show me how it can cause problems in the code.
9 Replies
Pobiega
Pobiega3mo ago
This is a pretty poor example of DI if you ask me the core idea is that you pass dependencies in (usually via the constructor) to things, instead of letting them create them for itself compare and contrast these two
public class NoDI
{
private readonly IServiceOne _serviceOne;
private readonly IServiceTwo _serviceTwo;

public NoDI()
{
_serviceOne = new ServiceOne();
_serviceTwo = new ServiceTwo();
}
}

public class WithDI
{
private readonly IServiceOne _serviceOne;
private readonly IServiceTwo _serviceTwo;

public WithDI(IServiceOne serviceOne, IServiceTwo serviceTwo)
{
_serviceOne = serviceOne;
_serviceTwo = serviceTwo;
}
}
public class NoDI
{
private readonly IServiceOne _serviceOne;
private readonly IServiceTwo _serviceTwo;

public NoDI()
{
_serviceOne = new ServiceOne();
_serviceTwo = new ServiceTwo();
}
}

public class WithDI
{
private readonly IServiceOne _serviceOne;
private readonly IServiceTwo _serviceTwo;

public WithDI(IServiceOne serviceOne, IServiceTwo serviceTwo)
{
_serviceOne = serviceOne;
_serviceTwo = serviceTwo;
}
}
this is what "inversion of control" is all about instead of the service creating and managing its own dependencies, they are passed in with DI, we manage these dependencies and their dependencies etc with a DI container/provider
SleepWellPupper
SleepWellPupper3mo ago
Note that if you only have one implementation of a dependency, you don't have to abstract it into an interface unnecessarily. Just inject the implementation as is. Use interfaces if you need to, e.g. testing/mocking, actually having multiple implementations etc.
textYash
textYash3mo ago
@Pobiega & @SleepWellPupper thanks! That helps. What real-world problem would make me feel the need for DI? (because I still feel a lack of clarity in my knowledge which I believe would be met once I solve a real problem with DI)
Pobiega
Pobiega3mo ago
honestly, just building any non-trivial app while being aware of the problem it solves you see, the most common problem you face with the NoDI type code I pasted above is.. how do you deal with shared lifetimes? what if I have a ServiceOne that I need to re-use, but I want ServiceTwo to be new every time? or worse, what if I need it to be the same for a given thing (maybe a web request lifetime?) but if a new request comes in, that should have a separate instance of that dependency DI solves all these
textYash
textYash3mo ago
public class NoDI
{
private readonly IServiceOne _serviceOne;
private readonly IServiceTwo _serviceTwo;

public NoDI()
{
_serviceOne = new ServiceOne(); // ServiceOne that I need to re-use
_serviceTwo = new ServiceTwo();
_serviceTwo2 = new ServiceTwo(); // want ServiceTwo to be new every time
}
}
public class NoDI
{
private readonly IServiceOne _serviceOne;
private readonly IServiceTwo _serviceTwo;

public NoDI()
{
_serviceOne = new ServiceOne(); // ServiceOne that I need to re-use
_serviceTwo = new ServiceTwo();
_serviceTwo2 = new ServiceTwo(); // want ServiceTwo to be new every time
}
}
like this oh okay wait now I get it... that service one inside NoDI can't be reused to be passed to other methods right?
Pobiega
Pobiega3mo ago
exactly and manually handling this will quickly get annoying
textYash
textYash3mo ago
yea new new new new extravagant lol
Pobiega
Pobiega3mo ago
yeah, and if you ever decide to change your mind, you now need to rewrite half the app D: but with DI, you just change a single line of code
services.AddSingleton<ServiceOne>(); // always reuse same
services.AddScoped<ServiceTwo>(); // re-use same within same scope.
services.AddTransient<ServiceThree>(); // new every time its requested
services.AddSingleton<ServiceOne>(); // always reuse same
services.AddScoped<ServiceTwo>(); // re-use same within same scope.
services.AddTransient<ServiceThree>(); // new every time its requested
textYash
textYash3mo ago
Cool Thanks a lot again!
Want results from more Discord servers?
Add your server