Make an Object for That

Published on 4 February 2010
This post thumbnail

"I wanted nothing else than to make the object as perfect as possible."

  • Erno Rubik

I recently enhanced one of my search functions to support additional criteria. It was originally a very modest search, accepting only an account number and amount. But, over time, search fields and flags were added and the primary method grew to accepting a very long parameter list, and then to receiving a tightly-coupled dictionary of search keys and their values. I looked at the method and thought, "what idiot wrote this thing?"  That'd be me.

It's an easy trap to fall into. Layering new functions atop an existing O-O system requires ongoing diligence to refactor and maintain clean design patterns. The temptations are many; for example, standard objects like those workhorse collections can lure one away from crafting valuable new custom classes. But there are defenses, such as a good library of xUnit test cases to support refactoring. Good design and test-driven development pay dividends long after the original code is written.

In this case, a command object did the trick. Have a findPersons* method that takes far too many parameters?  Create a PersonSearch or PersonQuery command/value object to house them.

There are many benefits to this besides cleaner code. xUnit test cases usually come easier and are less brittle. You can better factor behaviors such as validation and conversion onto the command object itself. This can improve reuse across the client and server.

A basic security requirement for any rich web or client/server system is that validation occurs both at the client and at the server. If the command object is commutable, this means you write the validation code once, to be used on both sides. This works well with Google Web Toolkit (GWT), Server Smalltalk (SST), and similar frameworks.

This sent me on a brief witch hunt; for example, I searched for related methods with too many parameters: MyClass methodDictionary do: [ :ea | ea parameterCount > 3 ifTrue: [ Transcript cr; show: ea printString ]]. I found and fixed a few, and toyed with a Smalllint rule for it. It never seems to end with the perfect object, but often much closer.