I know how to use dependency injection, but I see no practical advantage for this

About this (enter the dependency)

private readonly ICustomerService _customerService; public Billing(ICustomerService customerService) { _customerService = customerService; } 

compared to this (create dependency)

 private readonly ICustomerService _customerService; public Billing() { _customerService = new CustomerService(); } 

The last sample, so to speak, is bad because ... it violates the DI ... of course, nothing is entered ... but what if the DI did not exist, which is so bad that the CustomerService is created manually from the Billing class? I see no practical advantage regarding the exchangeability of the service interface.

I ask for a practical example with the source code, whether it could be a unit test or show a practical solution why it is a much more loose connection.

Anyone who wants to show their DI muscles enough and why does he have a practical right to exist and use?

UPDATE

So, people do not read everything, I will write here my short experience:

DI as a sample has practical applications. To follow the DI without inserting all the services manually (a bad DI DI tool, so they say ...) use a DI infrastructure like LightCore / Unity, but be sure to use the right tool for the job. This is what I did not do ;-) Developing the mvvm / wpf application I have other requirements that the LightCore / Unity tool could not support, they were even a barrier. My decisions were to use MEFEDMVVM, which I am happy with. Now my services are automatically entered at runtime not at startup time .:-)

+6
source share
4 answers

Imagine that CustomerService connecting to your CRM system and your database. It creates a whole network of network connections to retrieve client data and, possibly, reads additional things from the database to increase them before returning data to the Billing class for use in its calculation.

Now you want the unit test Billing verify that its calculations are correct (you don’t want to send the wrong bills correctly?)

How will you unit test Billing if its constructor is attached to a class that requires a connection to a real CRM system and database? Wouldn't it be better to add this dependency as an interface, easily allowing you to provide a mock version for your tests?

This is why DI is useful.

+4
source

Understanding as well as understanding why are two different things.

One of the biggest advantages of DI is for unit testing. In your second example, unit test billing is impossible without testing ClientService (as well as checking for any additional dependencies in the chain). In this case, you do not test the device, you conduct integration testing! If you want a good rationale for using DI, you don't need to look for more than a rationale for unit testing.

+5
source

DI Comes when you want to pass various implementations of an interface to your class, for example: Unit Testing.

Suppose your Billing constructor is an MVC controller constructor, and your CustomerService used some form of IDataContext as a parameter.

Global.asax

 // Does the binding ICustomerService binds to CustomerService IDataContext binds to EntityFrameworkContext 

CustomerService

 private IDataContext _datacontext; public CustomerService(IDataContext dataContext) { _dataContext = dataContext; } public AddCustomer(Customer entity) { this._dataContext.Customers.Add(entity); this._dataContext.SaveChanges; } 

MVC controller

 private ICustomerService _customerService; public Billing(ICustomerService customerService) { _customerService = customerService; } public ActionResult NewCustomer() { Customer customer = new Customer(){ Name = "test" }; this._customerService.AddCustomer(customer); return View(); } 

Say you want to unit test your services or controllers. You have to go through CustomerServices, but you have to go through a fake EntityFrameWorkContext implementation. Thus, the FakeDbContext that implements IDataContext is passed to the client services.

FakeDbContext can simply store objects in lists or a more complex storage mechanism, so you can enter various dependency implementations, which allows you to change the behavior of one component without having to change the code in another place.

+3
source

In my experience, this is not only about eliminating the integration test (but this is also a very important point). Creating instances of classes inside can create a lot of work module testing. A class, such as CustomerService, may depend on an open database connection, configuration files, available services, and many other things that you should not be aware of when your task is to validate only the Billing class.

Speaking, sometimes it always hurts to enter everything. Injection frames can ease this load, but I'm not a big fan. Another kind of stackoverflow user pointed me to what he called a "hopeless injection". It basically consists of two constructor overloads: one constructor with an injected interface and one without it. One that does nothing but create a specific class that implements the interface, and passes it to another constructor. This happens something like this:

 public class Billing { ICustomerService _customerService; public Billing():this(new CustomerService()) {} public Billing(ICustomerService customerService) { _customerService = customerService; } } 

Thus, you have a way to implement when testing AND a way to create a class with the default interface implementation. Not everyone loves this model, but I find it practical for some scenarios.

+1
source

Source: https://habr.com/ru/post/896914/


All Articles