Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How far to take dependency injection?

Having recently discovered dependency injection I'm now trying to get to grips with how often and how far to take it.

For example, let us say that I have a dialog which prompts a user for their registration details - first name, last name, telephone number, a serial number - that sort of thing. The data should be validated in various ways (e.g. that the first and last name aren't empty, that the serial number is a certain length). Once validated, it should be cached on the local machine and also sent to a registration server. The dialog should only close once all of those things succeed, or the user cancels.

So that's maybe four things we're trying to achieve here (responsibilities): UI, validation, local caching, sending data to a non-local server.

What is the dialog's responsibility and what should be injected? Obviously the dialog does the UI, but should validation, caching, and data sending all be injected? I'm thinking they do, otherwise the dialog class has to know about the logic behind the data fields in order to do validation, it has to know how and where to cache data, and also how to send the data somewhere. If so, this can lead to some hefty code at the caller's end (assuming we're doing injection via the constructor, which I think is preferable to setter functions), e.g.

MyDialog dlg(new validator(), new cacher(), new sender());

But perhaps that's ok? It does look a little alien to me right now after years of seeing code where things like the dialog do everything. But I can also see how this escalates quickly - what if there are all sorts of other small things it needs to do - how many things being injected becomes "too many"?

Please don't try to pick holes in the example scenario, I'm just using it for illustration. I'm more interested in the principle of DI and at what point you may be taking it too far.

like image 215
WalderFrey Avatar asked Nov 08 '22 18:11

WalderFrey


1 Answers

Well you certainly can do that. Injecting validation makes a lot of sense because then you can write unit tests around your validation code which don't have to fire up any GUI components in order to work. Injecting caching makes sense because then the dialog doesn't have to know anything about the caching system beyond its interface. Injecting a sender makes a lot of sense because your dialog doesn't have to have the foggiest idea where anything's going.

I have a habit of splitting things out quite heavily, because I like the single responsibility principle and I like writing code that's as pure as possible.

The problem is when you inject interfaces which are too big, so you no longer have any reasonable idea which bits of those interfaces the thing you're injecting into might actually need to call, and the interactions get complex and your unit tests start relying on precisely what gets done with the dependencies because you can't be bothered to mock out the whole interface when you know 75% of it won't be used.

So, do inject things which are clearly separate responsibilities, but make sure you design their interfaces in a suitably constrained manner. Classes can implement multiple interfaces simultaneously, so it's not like you can't slice up interfaces into small bits but implement them all with the same object if you want to. The dependent code never has to know!

As for when you're taking it too far... difficult to say really, but I don't think you get to that point until you're injecting something using an interface which adds nothing at all. I would always want to inject things which have side effects, because that's a massive aid to unit testing and to keeping things more reasonable. If you can split out business logic into pure classes and inject those you're going to have an awesome time writing unit tests for it, so that's probably worth doing.

I use a test something like this:

  1. does it do I/O and I'm not already inside an I/O providing class? Inject it.
  2. does it provide self-contained processing that I don't need to know the details of? Inject it.
  3. does it do something which isn't part of my single responsibility? Inject it.

Your mileage may vary.

like image 138
Matthew Walton Avatar answered Jan 04 '23 01:01

Matthew Walton