What is dependency injection and when should I realistically use it

Recently I've got some critics from people saying I should not create a Helper / Utils class with a bunch of public static methods and that I should rather use DI practices for it For me my approach makes sense since my Utils are only supposed to exist once in the whole program so I can access it from everywhere So what exactly is bad about my approach?
10 Replies
Poller
Poller6mo ago
im going to make a few assumptions here, so if i am wrong in anything please dont feel offended. - if you ask this question it sounds like also things like SOLID are not much known. so this seems not to be a partial issue, but a more breader from i think lacking experience.(DI alone might not change much in terms of impovement for you) - if you create code that is structured and uses DI you can propperly test your stuff. (a static function call can not be mocked, but if you inject a service (interface!!! ) you then in unit test can mock that external service call. - using static methods/classes binds everything to that implementation. forever. thats a small cut of how i see this. feel free to ask if i didnt explain something clear.
Pobiega
Pobiega6mo ago
There is a time and a place for static utility methods. Specifically, if they have no dependencies at all and are entirely stateless, they should probably just stay a static method. However, if your helper/utility has some dependencies, meaning other objects it relies on for some reason (maybe for writing to a stream, or sending something to an external http service) thats when you might want to refactor and turn your helper into a service you can inject via DI thats because the dependency for that service can then also be injected, and you have all your object/dependency lifetime management in one place
The Fog from Human Resources
My Utils classes mostly only contain things like strings (for example a base URL), paths or things like conversion functions
Pobiega
Pobiega6mo ago
thats probably fine then if the base url has no need to change, its perfectly fine to have that in a const string somewhere
The Fog from Human Resources
Reading over this again: So you say a class or collection of functions that for instance makes API calls should not be static That's my approach, I usually make API Client classes, each of them have their own headers, own Auth keys or whatever the api needs and their own public functions that only work in an instance so each client is only concerned with its own calls Wouldn't this Client object describe DI as in what you said about the Http thing?
Pobiega
Pobiega6mo ago
you certainly shouldn't have your API key as a const/static readonly also, a method that does an API call needs a HttpClient instance. If the method is static, where are you getting that from?
The Fog from Human Resources
thats why each API Client is holding its own HttpClient
Pobiega
Pobiega6mo ago
Not the worst idea ever, but also not how you'd see that implemented most of the time since a HttpClient is an object with internal dependencies and non-trivial lifetime, I'd probably recommend injecting it through HttpClientFactory (withMicrosoft.Extensions.Http) but a static httpclient works okay, especially if you only have a very low number of these API clients
LasseVK
LasseVK6mo ago
Static methods are absolutely the way to implement things, unless you feel there is a need to be able to depend on "something that does X", except that you don't want it to actually do X. This in particular comes up during testing. For instance, if you have a method that says "File.WriteAllText", that writes a string to a file, replacing it if it exists, and you want your own code to depend on "something that (re)writes a file with the text I supply", then for test-purposes, using a static method call is not a good idea, as you will have to depend on an implementation that actually writes something to disk. Instead, that might warrant an abstraction, and interface or class that you can depend on that will write to the file on disk, but which you can substitute during testing with an implementation that does not write to the disk, but instead writes to memory, or just observes that the write was attempted.
PixxelKick
PixxelKick6mo ago
100% agree, I refer to such things as Egresses now, and then how Data comes in as the Ingress. Egresses would be stuff like: 1. Writing out to a file 2. Invoking an HttpClient 3. Sending packets to a Udp server Luckily a few of these things have very easy ways to mock them without extra effort. HttpClient has a built in way to do it, you can just use a StreamWriter you write to for the filestream (and instead pass in a memory stream in your test), etc etc For Ingress that's pretty much just the oarams of your method you are testing which is easy as well.
Want results from more Discord servers?
Add your server