20040817

Getter-based is the mother of all dependency injection

A while ago I wrote the blog Containers & their injections, and I have noticed that arguments still rage on the merits of the various types of IoC paradigm.

Using a getter based pattern gives you full control of how to manage your injections. You are then free to map this to any of the various types, even those that use AOP.

So for example you have:


abstract class MyThingy {
final void doMyThingyThing() {
...
serviceThingy = getMyRequiredServiceThingy();
...
}
protected abstract ServiceThingy getMyRequiredServiceThingy();
}


Note the lack of a constructor and that it uses the classic getter style. You can use this class directly for getter based injection using AOP, but you can also use constructor and setter based approaches using subclasses.


class MyThingySetter extends MyThingy {
private ServiceThingy myRequiredServiceThingy;
protected final ServiceThingy getMyRequiredServiceThingy() {
return myRequiredServiceThingy;
}
final void setMyRequiredServiceThingy(ServiceThingy thingy) {
myRequiredServiceThingy = thingy;
}
}


So you can now support getter based and setter based, but the class is not final and so you can subclass to the next step.


final class MyThingyConstructed extends MyThingySetter {
MyThingyConstructed(ServiceThingy thingy) {
setMyRequiredServiceThingy(thingy);
}
}

or even for static relationships

final class MyThingyConstructed extends MyThingy {
private final ServiceThingy myRequiredServiceThingy;
protected ServiceThingy getMyRequiredServiceThingy() {
return myRequiredServiceThingy;
}
MyThingyConstructed(ServiceThingy thingy) {
myRequiredServiceThingy = thingy;
}
}


A library that implements its service management in such a way, with the three levels of abstraction, is capable of being used in most common types of injection model. It is also capable of being used effectively in a factory pattern for those systems that are not container orientated.

3 comments:

Howard said...

I can speak from experience that people don't like the fact that their classes are abstract. I believe that this approach to DI is derived from Tapestry 3.0's approach to creating properties. Yes, the flexibility is good, but the cost (in terms of a proliferation of classes, even if just for testing) is high, and the frustration of not being able to instantiate your own classes is hard to dismiss.

I'm often surprised by this aspect of DI. I vastly prefer setter injection, and the setter methods are not part of my service interface and are completely inaccessible to client code, even via reflection, due to the intervening layers of proxies and interceptor objects.

Anonymous said...

Yuck! Yuck yuck yuck! Yuck yuck yuck yuck yuck yuck yuck!

Dude, get some training in basic OO design skills. If not for yourself, do it for the poor saps who will have to maintain your code when you've gone.

straun said...

In response to the comments:

Howard, you need to check your alarm clock - DIP suggests this approach and that has been around since 1996 (Tapestry started in 1999?) and 'services through extension' had been a HOOD technique for at least 5 years before that. But yes there was a blog on this particular technique around the time of the big DI meeting.

I did not want to repeat my previous blog too much on this subject. That blog entry suggests using velocity to generate these extensions. With generated code I have fewer qualms about using a more open extensive class structure, and in this case I suggest that there is a business requirement for being able to support multiple container paradigms. I am not suggesting that any particular library should do this, I am merely pointing out that you can keep your IoC options open.

I know inheritance is evil and if there are any dude children watching out there then they should ask an adult before attempting any of these activities (including finals). But in this case it is necessary as providing a delegating/aggregation implementation itself requires an injection, and as I said in my previous blog something like 'these things should be as close to normal Java as possible'; I chose flexibility over ease of maintenance in this case. I concede that being unable to instantiate is a drawback.

Testing all your classes no matter how trivial is a noble endeavour, I wish anyone who does this luck. It is however the second most ineffective way of testing your system (after not testing at all) - expect a blog on this as a separate subject.

My words above are intended to presuade people to use DI as a technique because of its obvious benefits. The readers who are worried about which type of IoC technique they should use are my real intended audience and my message is "use it, it won't bite, and here is a way you can hedge your bets a bit".