Saturday 22 November 2014

My answer to a question on Stack Overflow about DI

G'day:
I'm re-posting this here, hoping to get feedback from the DI doyens like Sean, Brad and Luis. Plus it's an easy article to post whilst I get together my article about Dave's Python code (from the quiz: "Something for the weekend? A wee code quiz (in CFML, PHP, anything really...)").

The question is here: "Dependency injection is only for testing?", and I'll reproduce it here (I'll ask the OP if this is OK, and remove/summarise if not):



I read many times that hardcoding object is not a good practice:

class Session
{
 private $user;

 function __construct()
 {
  $this->user = new User();
 }
}

its bad only because of its untestable behaviour? I simply find this hardcoding easier to read. Of course I can add those methods to be DI-like:

public function setUser (User $userObj)
{
 $this->user = $userObj;
}

public function getUser()
{
 return $this->user;
}

But then its like a house where even the trussing can be changed. What for?

Excellent question.

My response was this:

Unlike a house, which is a physical object and where the trussing can't be changed, this is code, wherein things are less permanent.

To be honest, having used DI for years I think it's overused. Quite simply: a bunch of a class's dependencies won't change, and if they do, it's easy enough to then apply DI to them.

Sticking with the house analogy, my position is becoming "don't necessarily build the entire house extension now, but also don't build a load-bearing wall where a door might need to go". I'd create your dependency objects in your constructor, so if it ever becomes necessary to make them swapabble, it's easy enough to add a constructor arg and start passing the dependency in via a DI framework if needs must.

One thing you're leaving yourself open for then is that every instance where that class is created will then need to be replaced with a DI-friendly version, which means a lot of refactoring, and accordingly a lot of regression testing. This is a heavy risk. If you make sure you've got very thorough automated test coverage, a lot of this risk is mitigated.

On the other hand... using a DI framework from the outset, even if the dependencies aren't injected from the outset will mitigate this consideration even more, when it comes time to need to inject them

Using DI certainly does make your testing a lot easier, which in turn will inncrease your ability to achieve higher test coverage. But I also don't think one should code specifically for testing.

So there's a few things to weigh-up and apply to your specific style and requirements when making this decision.

What do you think about this?

--
Adam