Creating Service Objects And Value Objects In A Dependency Injection (DI) Framework
Posted May 10, 2012 at 11:19 AM by Ben Nadel
This morning, I found a very interesting article by Misko Hevery on the use of the "new" operator for object instantiation. In his article, Hevery lays out some guidelines that can help keep your code cohesive and easy to test.
First, he asserts that you should try to avoid calling the "new" operator as much as possible within your code. Every time you call the "new" operator, you make your code harder to test because you have object-creation logic encapsulated in such a way that your testing framework cannot easily manipulate it. When possible, the "new" operator should be deferred to some sort of Factory that can use a Dependency Injection (DI) framework.
That said, there are types of objects that the dependency injection framework simply can't know about. He refers to these objects as "Newables." For this blog post, I'll refer to them as "Value" objects. And, I'll refer to the other objects (the ones the DI framework can know about) as "Service" objects. These Value objects must be created inline using the "new" operator.
In this context, Hevery lays down the following guidelines:
- A Service object can ask for other Service objects in its constructor.
- A Service object can never ask for a Value object in its constructor. This is because the dependency injection (DI) framework will not know how to create the required Value object.
- A Value object can ask for other Value objects in its constructor.
- A Value object can never ask for a Service object in its constructor.
- A Value object can be passed a Service object as part of a method call; however, that Service object cannot be stored in the state of the Value object. In other words, the Value object cannot retain a reference to the Service object.
I don't know enough about Object Oriented Programming (OOP) to know whether or not these guidelines make complete sense. I can understand that removing dependencies will probably make your classes more cohesive as the potential for "responsibility" has been limited. There are probably situations where these guidelines must be broken; but, like the Object Calisthenics exercise, leaning on these guidelines for "advice" will probably have beneficial effect on how you think about your code.
If nothing else, running into a situation that violates these guidelines might just give you pause to ask the question, "Is there a cleaner way to wire this together?" And, the more we question our status-quo, the more likely we are to find better ways of doing things.
I think we should always be a little weary of taking advice straight out of Java. Thankfully, ColdFusion isn't Java and the fact that CFCs don't have an automatic constructor means that we *can* use object factories for our VOs.
The calling code can get an uninitialized object from your object factory and call the VO's init() method with the arguments that it only it knows about. So:
var myVo = voFactory.getVo( 'myVo' ).init( foo=bar );
I believe that there should then *never* be a need to use the new operator in ColdFusion if you are using object factories.
Odd that I'm currently working through much the same issue while developing some standards for Unit Testing. And with much the same recommendations: heavier reliance on DI and factories while avoiding objects instantiated within a method using the "new" keyword.
As a result, though, our unit tests are getting much more accurate and we have a solid foundation to begin TDD.
As far as the validity of much of this, I simply don't have the experience to say. All I can say is that I don't know enough and I'm willing to do a lot of experimenting.
As far as the Value Objects and the DI framework, I don't get the sense that he was talking about serving up the class definition via the DIF. I think it was more like the DIF cannot init() the class.
Very cool to get some ColdFusion / real-world points of view. I'm excited to be challenging myself to think differently. I really should also start getting into TDD - I don't know much about tests yet :(( shameful :)
@All, the two quotes above came from this blog post:
@Ben, I *think* that he was very much saying that you can't serve up your VOs through the DIF. I believe his reasoning being that you cannot create an object without invoking its constructor in Java (folk correct me if I am wrong), so that a DIF wouldn't automatically know how to create the VO and you couldn't do the example I gave in Java.
That could very well be what he was saying - I am in no place to argue for or against it. That said, having the class definition encapsulated in the DI / factory would make the embedded instantiation more testable (as far as I can see).
@Ben, yeah more testable + more controllable in terms of how you decorate objects and how you switch in different implementations, etc.
I'm sure if he could, he'd never use 'new', just that given the language constraints he is somewhat forced to.
I probably came over a bit rude with my we're not Java comment (unintentionally) but I think its something we should always keep in mind - we can avoid all the things that make coding in Java a PITA.
Absolutely no "Attitude" detected! We're just having a good conversation about awesome programming stuff. You think I have any idea what I'm talking about on this matter?! I'm just trying to wrap my head around it.