April 04, 2004
Cut With the Grain
When possible, cut with the grain. The grain tells you which direction the wood wants to be cut. If you cut against the grain, you’re just making more work for yourself, and making it more likely you’ll spoil the cut.
I was working on a Java web application the other day, looking to move functionality out of base classes (where they were painfully tying unrelated actions into a rigid class hierarchy), and into the filter/interceptor chain that delivered data to the actions.
It’s dependency injection again, but the inversion that gets its cue from the contents of the incoming request. So, for example, one thing the Interceptor could do is see that since the request has a personId parameter and the action has a setPerson() method, it should retrieve the appropriatePerson on the action’s behalf.
This is actually pretty neat: it avoids cumbersome implementation inheritance, encourages you to use consistent parameter naming across the app, and makes actions a lot easier to test since now instead of having to mock out the retrieval of a person, you can just pass one in.
Java put one little obstacle in the way. Java has no simple way of asking “Does this class respond to this method?” I could instead try Class.getMethod(), but that quite annoyingly throws an exception if the method isn’t found, rather than just returning null. Or I could search the array returned from Class.getMethods() to see if there is a setPerson() contained within.
We’re only talking about a few lines of helper method here, of course. Apache Commons already has one ready-made and waiting for me. All this would be doing, though, is moving the ugly code into a ghetto so I wouldn’t have to see it. It would still be there.
I was cutting against the grain of the language. Java is statically typed. Java doesn’t want me to use the existence of a method to determine if I should call it. Philosophically, the language considers the existence of a method with the right name to be a coincidence, unless that name is backed up by a specific type. (Witness the objections to dynamic typing that start: “What if I had two methods called shoot, one on a Camera object, the other on a Gun?)
For Java, the correct way to do what I was trying to do was to avoid introspection entirely, and instead define a PersonAware interface that I could check for with instanceof and then cast to directly.
It’s a pattern I follow quite often. Over the last few years I’ve come up with a series of very clever, rather complicated introspection-based solutions to problems. Invariably, a cooler head has later either deleted them, or convinced me to delete them because while the Java way might be slightly less powerful and not as conceptually clean, by moving the code back to cut with the grain of the language, it became more readable and more maintainable.
On the other hand, Ruby is very much a dynamic language. When I’m writing code in Ruby, I expect duck typing to be the norm. If it looks like a duck and quacks like a duck, we treat it like a duck.
Which is why I was annoyed the other day. Some Ruby library told me it needed a Hash passed in, and I made the quite justifiable assumption that this meant it wanted some object that implemented certain methods of the Hash class. As far as I could see, it only really needed a [] method, which my MultilayeredStuffHolder already implemented. When I passed my object into the library, however, I got an error back because the library explicitly checked the class of my StuffHolder, and complained that it was not a Hash.
Against the grain, again.
On the other hand, when carving meat you carve against the grain. The carving knife is stronger and sharper than your teeth: carving the meat against the grain leaves your teeth a much easier job, and the meat feels more tender as a result.
A lot of very useful Java libraries carve directly against the grain of the language. Hibernate. Groovy. Spring. Pretty much anything that makes use of cglib. Sometimes you have to carve against the grain to cut a large problem up into easily chewed pieces.
At this point, a Lisp programmer might attempt to convince me that the best path is a language with no grain at all. Such malleability can be hard to get one’s teeth into, though.
Posted to nerd, rambling aimlessly, stories at April 4, 2004 10:51 PMLisp is like gum. It has no grain, and it's maybe fun to chew on for a while, but you're certainly not going to swallow it...
Posted by: Jason Carreira at April 5, 2004 04:54 AM (#link)Charles I think you got it backwards. Any C++ programmer will tell you that there's a lot said for 'dynamicity' even in the most statically typed language. If you think using things like reflection and cglib are contrary to OO or 'hacks' then you've missed the point. Reflection and cglib work so well precisely because Java is so statically typed. The fact that it's possible for a computer to examine a class's interface and 100% determine for sure that it requires a Person is a strength of OO and something that you can't do in weakly-typed languages. As for 'PersonAware' I've always felt that was an ugly hack and not OO. Interfaces like XAware aren't real interfaces because they don't say anything about what an object is/does--they're not part of the object's "real" public interface. Such things belong in metadata because that's what they are really, implementation details that the clients of a class (other objects who use the Action) don't care about at all.
Posted by: Bo at April 5, 2004 05:12 AM (#link)Guys with a Lisp that can swallow .. Jason, what are you talking about? ;-)
Posted by: Cameron at April 5, 2004 10:20 AM (#link)Bo, I think the word you're looking for is "dynamism" ;o) But generally I agree with you. Something like this is to do with the *implementation*, not the object itself in "business terms", so I'd be inclined to do this via a method / helper class rather than an interface.
Posted by: Ben Poole at April 6, 2004 01:00 AM (#link)Since the meme seems to be "torture the metaphor" today, I'd like to add that it's one thing to cut with the grain or against the grain, but dealing with knots and gristle is something else entirely.
Posted by: Gordon Weakliem at April 6, 2004 09:27 AM (#link)"Torture the metaphor" is a tortured metaphor.
Posted by: Charles Miller at April 6, 2004 10:05 AM (#link)