LightWire, Constructor Injection, And Inheritance

LightWire, Constructor Injection, And Inheritance

Posted by Brad Wood
Nov 08, 2008 18:29:00 UTC
As we push ahead with our app using ColdBox and LightWire, we have been configuring our Dependency Injection as we go. DI is one of those things I'm pretty certain I see the benefit to, but I'm not sure how sold on it I am. At this point, we may only have 50 to 60 CFCs so perhaps we're just not deep enough in to see the real profit yet. The only real advantage I've seen to date is having all of our dependencies spelled out in one configuration file (with the exception of the stuff you are auto-wiring). Other than that, I don't know that I have really saved any code. That being said, I don't have any circular dependencies, and my dependency levels don't get much more than 2 or 3 levels deep right now so maybe I just need to be patient.If for no other reason than to save on code, we have been using constructor injection over setter injection. Even Martin Fowler states that he prefers constructor injection over setter injection if possible in order to more clearly define what is necessary to create a valid object. ("Constructor versus Setter Injection" section) Also it allows you to keep your injected properties immutable by not having to expose public setter methods for them. Constructor Injection requires that you accept each injected property during instantiation of your component as a parameter to your constructor (generally init()) and subsequently set it into your CFC (preferably in the variables scope to make it a private property). This will cost you about two lines of code in your CFC. Setter Injection requires that you provide a public setter method to set each necessary property into the object. The object is created in one step, and then the setter methods are called to finish populating the object. This approach requires you to code all of the setters, but it is the only way to satisfy circular dependencies (where two components require a reference to each other). One of my first problems with DI is that I can't directly create my transient objects, but rather need a reference to my object factory (LightWire in this case) to create my objects for me and return them already stuffed full of dependant goodness. Of course, that fact in itself isn't the issue. I have decided that all transient objects will be created inside of the appropriate service component. (My Event Handler asks the userService to track the current user, which creates an instance of the user object for storage in session) Since LightWire is baked into ColdBox, I must inject a dependency to the ColdBox framework into each of my services. There goes the concept of a loosely coupled controller and server layer! So, on to the original topic of this post: constructor injection and inheritance. For the sake of an example, let's consider two components-- widget.cfc and customWidget.cfc. customWidget is a specialized form of widget, providing expanded functionality and additional properties over the base class. Therefore, customWidget extends widget and its init() follows the familiar super.init() pattern. Let's say that widget has widgetDAO, and widgetService injected into it via a parameter to the init function. The customWidget class has an additional dependency to the doohickeyService component. Now, what happens on instantiation of a customWidget object is LightWire only calls customWidget.init() and that one method is the only window of opportunity that our DI engine will have to inject in the necessary pieces. This means that the customWidget.init() method needs to accept not only the doohickeyService object, but the widgetDAO and widgetService as well which it will promptly turn around and pass into the super.init() call. The long and short of this is that constructor injection mandates a component have knowledge of all its ancestor components' dependencies and be prepared to accept all those dependencies and pass them on. Maybe it isn't so bad for a class to know a little about the class it is extending, but it makes me uncomfortable. In addition to the programmatic duplication, my DI configuration also has duplicated pieces. Since widget and customWidget can both be instantiated directly, I must set up a cascading list of dependencies for my child classes that include all the dependencies of their supers. This means that a added dependency to the base class needs to be reflected all the way down the line. It is my understanding that ColdSpring deals with this scenario (as well as other entire non-inheritance related duplication of configuration) with the parent attribute in its XML config which allows you to automatically assume the dependencies of another component. I like that to a degree, and I'm sure LighWire could be easily enhanced to do the same, but it still doesn't keep be from having to account for it in my actual components' inits. Discuss.

 


Bob Silverberg

Thanks for the informative article, Brad. I've read discussions of constructor vs. setter injection before (mostly on mailing lists) and this is the first time I've read any arguments more concrete than "it's a matter of personal preference", so kudos.

Because I use Coldspring and I sometimes have circular dependencies I tend to always just use setter injection. After reading your post I think that perhaps constructor injection would be more appropriate in many cases.

Regarding your comment on transients and your model being tightly coupled to the ColdBox framework, could you not create your own Transient Factory, and then inject that into your services? You could then just inject a dependency to the ColdBox framework into that Transient Factory, or perhaps you could even get a direct handle on LightWire without using the ColdBox framework (I know very little about ColdBox and arguably less about LightWire), and inject that into your Transient Factory. Either way, your services are no longer coupled to either ColdBox or LightWire. If you decide to change your DI engine in the future, or even dispense with a DI engine entirely, all you'd have to change is the Transient Factory.

While we're on the subject, Paul Marcotte and I wrote a Generic Transient Factory that is quite flexible, although it does rely on ColdSpring to inject dependencies into transients. I think he was planning a blog post about it, but got busy.

Matt Quackenbush

@ Brad - When I first started learning about the principles of OO, in an effort to make sure that I understood what I was learning, I went about building everything on my own from the ground up. One of those items I built was a DI engine. When I put it together, one of the goals was to be lean and mean, and fast. With this goal in mind, I required any dependencies to be done via constructor injection. I achieved the goal; it was indeed lean, mean, and very fast.

At the urging of several people, I decided to then go and play with ColdSpring, and in doing so decided to experiment with setter injection. All I can say is, WOW! After spending a couple of years dealing with all of the duplicated code to which you refer, and building recursive methods to try and cut down on that code, and all of that mess, I feel like a free man now that I have setter injection on my side.

So for me personally, having played extensively on both sides of that argument, I am a huge fan of setter injection for dependencies. (Now then, if we're talking simple strings, or a struct or array instead of an object, then I obviously still go with constructor injection.)

I have two additional things I would like to point out:

  1. As Flatware already mentioned, there is no need to tie your service layer to ColdBox, and, in fact, I would strongly discourage doing so. I second Bob's suggestion of a TransientFactory. Also, having not used LW yet (that's on the todo list) I'm not sure if it can do it, but with ColdSpring, if you have a setter for BeanFactory on your service, it will automagically inject itself into the service when it is created. You might check with Peter and/or Luis to see if LW will do this for you. If it will not, you might be able to create a workaround by defining a bean that points to LW, and having that as your property. Something like so:

<bean id="ColdboxFactory" class="coldbox.system.extras.ColdboxFactory" autowire="no" /> <bean id="MyBeanFactory" factory-bean="ColdboxFactory" factory-method="getPlugin"> <constructor-arg name="plugin"> <value>ioc</value> </constructor-arg> </bean>

  1. In ColdBox, if you are letting ColdBox handle your autowiring on things like your controller, you can use private setters to inject your dependencies. Very cool stuff indeed!

HTH

Bob Silverberg

One thing I forgot to mention regarding setter injection; if you are not autowiring you might be able to use onMissingMethod to eliminate the need to explicitly define your setters in your components.

Brad Wood

@Bob and @Matt: Thanks for the insight. The TransientFactorymakes perfect sense at the expense of throwing more CFCs at an attempt to manage CFCs. :)

Tying my framework (ColdBox) to my services doesn't bother me as much as maybe it should. I don't plan on changing my framework so I guess I don't mind. Originally we were going to just inject the IoC plugin, but then my coworker was like "Well hey, if we're going to inject that, we might as well reference the whole framework in case we need anything else". To date we haven't needed anything, and perhaps if we separate our MV and C properly we won't.

I am tempted to try setter injection simply to eliminate my inheritance headaches. It's good to know that ColdBox can make use of private methods to set dependencies in my handlers since they extend the framework. Unfortunately, that doesn't help my services and beans. Of course, I don't mind the public methods that much. I don't fret too much about the API I present. The fact that I am using IBO as a base class with uses generic get and set methods kind of ruins that already. OnMisssingMethod() might help with the setters, but I guess it only really takes 4 lines of code for a basic setter.

ike

These are the kind of fidgety little things that annoy me too... Ultimately this is the point at which you get seriously diminishing returns from trying to "solve" the problems presented. Basically what you can do, you've already done. It doesn't make it any of it any of it any less frustrating though. :)

ike

On the issue of coupling the ColdBox framework (or any framework) to your object model, here's my thought. Is it preferable not to do that? Yes. However in a situation in which it's pragmatic to reference the framework (or say just the IoC plugin), here's the way in which you minimize the dependency.

DON'T inject anything into your object. I know that sounds really weird, but it will make sense in a minute. Instead of injecting the framework or the IoC plugin into your object, simply create a couple of private methods to "get" whatever tools you need directly from the application or request scope. Again, I KNOW that probably every fiber of your being is shouting NOOOOOO to this answer, but trust me, this will REDUCE your dependencies (help eliminate coupling).

Within your component, use that method RELIGIOUSLY. Do not under any circumstances EVER reference the framework or IoC plugin or whatever it is you need from the external scope without using that function to get it. What have you done? You've provided very simple access to the tools the object needs.

What happens if the variable name changes, if it becomes stored in a different location (i.e. moves from application scope to request scope or vice versa)? You have only ONE place to make that change (in the function). What happens if you switch frameworks? What do you do then? You have ONE function to edit.

See what I'm getting at? It's precisely what OO programming was designed for. You've instantly reduced all your dependencies to a single function, which also makes stubbing it for TDD trivially simple, because all you have to do is write <cfcomponent extends="mycfc"><cffunctiuon name="getIoC" access="private">... get my stub ...</cffunction></cfcomponent> and viola! You've redirected access to the vital component to target your stub. :)

It's a good idea to have that private getter for that reason anyway even if you do choose to inject the object in the constructor, however, if you're doing that, then you're immediately increasing the number of dependencies from the potential 1 function that's easily overwritten to now having not only the 1 function, but also the constructor arg, which is much harder to overwrite or change or deal with in general.

Anyway I hope this comment makes sense and is somewhat helpful. :)

John Farrar

Great discussion. I like the use of setter injections with IOC also.

@ike, religiously! Considering most people's religion that would be very liberal! Tell people why and they will get the point. :)

Site Updates

Entry Comments

Entries Search