Object-oriented – Hidden dependencies – why not

dependency-injectiondependency-managementobject-orientedPHP

Hidden dependencies:

function __construct($dep_registry){
  $this->db = $dep_registry->get('db');
  $this->request = $dep_registry->get('request');
  ...
}

Not so hidden:

function __construct(Db $db, Request $request, ....){
  $this->db = $db;
  $this->request= $request;
  ...
}

The disadvantages of the last is that, if I need to change the class later and need another dependency, I have to change the constructor arguments. And if I change the constructor, I need to also change every file that uses it. With hidden deps the change would only be needed in the two places: the constructor and the registry class. Another fail of the 2nd method is that it's not possible to lazy load dependencies, i always have to pass the instances

So what's the disadvantage of hidden deps and why is it bigger than this disadvantage of not so hidden deps?

If you are using true Inversion of Control, then you shouldn't need
to. If you need to add another dependency to the class, you should
look back at what this class is doing. Perhaps you need a sub type
instead. Adding dependencies could be an indication that you are
breaking the Open-Closed Principal of programming (Open for extension,
but closed for Modification).

OK, dependencies should not be hidden was/is a principle too. Principles are all cute and all, but what do they solve in reality, besides not violating other principles? So why is breaking the Open-Closed principle bad (assuming that this is what adding another argument to the constructor does)?

Best Answer

As an outsider looking at this code, it is not clear how the "hidden dependencies" get created to begin with. What if there is no "db" dependency registered? Does it throw an exception, return null? If my class doesn't get a "db" dependency, how bad is that?

Many times people lean towards this pattern when there are too many dependencies to pass in the constructor without making the constructor look like complete garbage. If your class requires a large number of dependencies, consider using "setter injection" instead (for a property named "foo", call "setFoo(...)" to inject that dependency). Reserve constructor arguments for those dependencies that are absolutely required just to have the object exist -- not to use the object, but just to make it exist.

It also raises the question, is this class doing too much? Do you really need some of these dependencies injected at all? There is a balance between dependency injection and coupling. Too far either way and you create headaches for yourself.

Fine, but that still doesn't answer the question. Consider my class only has 1 argument, and on month later I need to add the 2nd one. So I still need to update the 346253453245 files that use my class, right?

If you are using true Inversion of Control, then you shouldn't need to. If you need to add another dependency to the class, you should look back at what this class is doing. Perhaps you need a sub type instead. Adding dependencies could be an indication that you are breaking the Open-Closed Principal of programming (Open for extension, but closed for Modification).

It's not to say you can't modify a class once it is written, but you should not modify the intended use of that class -- it's purpose.

Adding a new dependency tells me the purpose of this class has changed, and it's likely you need to break the class up into other classes, or create a child class extending your original one. That way existing code that does not need the functionality of the new class (and the new dependency) doesn't have to change.