June 03, 2004

Getter-based injection

Alright, I'm just back from vacation (report will be posted soon) and I have been following the reactions to my classification of getter injections with a lot of interest.  Just for memory, here it is again:

  1. Force the dependency to be passed to the constructor (not always possible and very invasive).
  2. Have the container invoke a setter, or set a field on your class (mildly intrusive since it forces you to specify a setter or a field you will never use yourself).
  3. Have the container provide the getter (not intrusive at all, except that now, you need the container for your application, which makes out-of-container testing problematic).

It looked like we had reached a stalemate:  option 3 looks like the best of both worlds except that it mandates the presence of the container, which is bad news for testability and brings us back to square one.

And then, Bob Lee came up to me and said "Why does the getter need to be abstract again?".  And he proceeded to put together a quick proof of concept, which has been discussed on TheServerSide since then.

It was one of these "doh!" moments.  Of course, the getter doesn't need to be abstract, but I guess my EJB bias made me overlook this fact.  Bob is right, there is absolutely no need for this getter to be abstract, and now we have an answer for all these Test-Driven Development weeni^H^H^H^H^H (sorry, had a Hani moment) advocates:  not only does getter injection work well with TDD, it makes TDD a first class citizen.

In the absence of a container, your class is testable right out of the box.  All you need to do is provide a default implementation for your getter that will work in your testing environment.  And if you run your code in a container, the getter will be overridden with a different value for production or staging.

How is that for the best of both worlds?

Posted by cedric at June 3, 2004 08:49 AM
Comments

We keep on hearing KISS and DRY bandied about. What is Dependency Injection getting us that factories don't provide?

All this talk about lightweight and "you don't need a container" when there are all these restrictions/prerequisites on class definitions to hook into whatever IOC container is being used(Constructor,setter, getter or container classes) just to placate TDD garbage seems pretty "heavyweight". We're just trading one pattern for another.

Posted by: Frank Bolander at June 3, 2004 10:33 AM

What is Dependency Injection getting us that factories don't provide?

IoC/Dependency Injection is simply a slightly cleaner approach than factories. You don't repeat the same lookup and cast code over and over. You don't have a dependency on the factory API; another developer can take my component and reuse it sans coding changes and not have to also take my factory and configuration method. With a factory, the dependency lookups are part of the implementation. With IoC, they become part of the interface, so others can see exactly what services my component depend on by simply looking at the Javadocs.

Posted by: Bob Lee at June 3, 2004 11:15 AM

It is not the best of both worlds. You skim over the most important detail for testing: "All you need to do is provide a default implementation for your getter..." That default implementation will be performed by either #1 (in the constructor) or #2 (in setters). So now you have re-implemented the exact environment you were trying to avoid plus you have ensured that your tests don't accurately reflect how the classes will behave in their container. I would have to say that it is the worst of both worlds.

Posted by: David L Kinney at June 3, 2004 05:59 PM

The getter should remain abstract since it's cleaner. I don't think that providing an implementation for a method whereas you perfectly know that method has to be overriden is a good design.
But even though the getter is abstract, nothing prevents you from writing a test class that extends your class and that provides an implementation for the getter.

Posted by: benb at June 3, 2004 10:47 PM

To Bob: Amen. Three times. Since using IOC I have cleaner code, I forgot about all those factory.createThing() and Singletons. My code looks cleaner and did I mention thac code metrics get close to perfection ? :-)

Posted by: Dorel at June 4, 2004 12:18 AM

I don't think its best of both worlds either. While unit testing my component I typically use mocks for dependencies and with this approach that seems quite ugly. A subclass that implements the abstract getter seems like a better alternative.

Posted by: Harish Krishnaswamy at June 4, 2004 06:31 AM

Dorel,

You are still dependent on a container unless you provide implementations or mocks. How is Girl.getKissable() not a factory method from a factory object(Girl). I like the cleaner semantics Bob mentions but it's still a Factory Object. It's doing a createThing().

Yes, the inclusion of a container makes code metrics cleaner because it abstracts a lot of coding redundancies but now I have a runtime framework dependency to deal with(and sell to operational support) and I'm moving code from one place to another either in a config file or a bootstrap class.

Posted by: Frank Bolander at June 4, 2004 11:41 AM
Post a comment






Remember personal info?