October 13, 2005

In defense of the nearly tautologic diagram

Aristotle Pagaltzis rallies against pattern hype in general, and Martin Fowler's dependency injection article in particular:

Where was the meat? I read and reread the definition, examined the flimsy code snippets carefully, stared at the nearly tautologic diagrams for roughly 10 minutes, trying to grasp the deeply profound idea but failing.

Aside from having snorted coke through my nose over "nearly tautologic diagrams" I feel the need to defend Martin Fowler's article because it had such a profound effect on me when it was published. Although I had been playing with "objects" and "classes" before, this article finally made me understand what OOP was all about. This is not true for many other articles and yes, I'm looking at you, shitty Car extends Vehicle OOP tutorial.

Aristotle is right when he says that the concept of dependency injection should be so ubiquitously understood that it shouldn't get its own brand name. But at the same time it represents the very essence of object-orientation and most people are not using it. How many times have you hacked around a library because it won't let you inject behavior? Once you've swallowed the pill, the world's ignorance of dependency injection is almost too much to bear.

I also think the more important of Fowler's postulations is not to understand dependency injection, but to use it with a persistence that borders on the religious. Consequently employing DI everywhere is actually a new methodology. Anyone who ever tried dependency injection as their default way of doing business knows that it is plain impossible with vanilla language constructs. Without assistance from a DI container you're just going to die the death of the million parameter constructor.

Comments

It seldom needs to be a "million parameter constructor." That usually comes from Type 3 purists.

A constructor should have parameters for those things that the object simply cannot function without.

All other parameters can have default values which can be overridden with setters. You can still specify the behavior if you want, but you don't need to if the default behavior is satisfactory.

My EJBs usually end up a mixture of Type 1, Type 2, and Type 3 IoC. I have the actual working class that uses Type 3 IoC (constructor parameters) for the collaborations and operating parameters that it simply cannot live without, and Type 2 IoC (setters) for settings that can be adjusted. For example, all of my classes have a "debug" flag that defaults to false, but can be turned on and off through a setter.

That working class is then wrapped by an EJB-shaped wrapper class. EJB uses Type 1 IoC (JNDI lookup), so my EJB wrappers obtain the dependencies from JNDI and then pass them along to the working classes through constructor parameters and setter calls.

I find the combination to be quite workable.

Posted by Doug (#)

For me it's either service lookup or constructor injection. The problem with setter injection is that it's too optional when you're handing dependencies through multiple layers of code.

Say you have class A which delegates mesages to a inner object from class B. Both A and B can be injected with dependency D. When you forget to have A pass on any injected D to B, B will incorrectly use the default implementation. With constructor injection this cannot happen, and while the service locator makes for some nasty coupling, it at least allows you to reliably switch implementations in my example.

Posted by Henning Koch [TypeKey Profile Page] (#)

Yeah, no question that forgetting to pass along a dependency "override" is the big problem with Type 2 IoC. I admit to doing it on a regular basis :-)

But personally I'm still happier dealing with the occasional forgotten override than in trying to remember exactly what order the million constructor parameters go in...

Posted by Doug (#)

But personally I'm still happier dealing with the occasional forgotten override than in trying to remember exactly what order the million constructor parameters go in...

At the end of the day, I'm all in favor of doing whatever floats your boat. It's not an exact science after all.

Posted by Henning Koch [TypeKey Profile Page] (#)

I lean towards constructor injection also, mainly because I live in a dynamic language, and those make it trivial to have your cake and eat it: you just write the constructor to take named arguments via a hashmap. (Some languages (dynamic and not) also offer a native mechanism for this.) In fact, in Perl it is customary to write constructors this way.

That way, there is no need to remember any argument ordering, and it’s trivial to make some arguments optional and have the constructor instantiate default dependencies for them. Then you also supply getters and, if appropriate, setters for the dependencies, and voilà, you have a maximally flexible design that does not particularly invite you to forget passing dependencies down either.

Posted by Aristotle Pagaltzis (#)

Post a comment

Thanks for signing in, . Now you can comment. (sign out)

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)


Remember me?