4 Simple Rules and Declarative Builders
Earlier this week I bought 4 Rules of Simple Design by Corey Haines, a short but interesting discussion of the Four Rules that came out of Extreme Programming. The book is grounded in Corey’s vast experience and in the code he’s seen people write at Coderetreat. It’s a quick but meaty read. There’s a lot of knowledge in the ~65 pages of content.
In a section on testing, Haines says:
In fact, over time I’ve developed a guideline for myself that external callers can’t actually use the base constructor for an object. Put another way: the outside world can’t use new to instantiate an object with an expectation of a specific state. Instead, there must be an explicitly named factory method on the class to create an object in a specific, valid state.
I’m not sure factory is the right word here. My (admittedly incomplete) understanding is that a factory is a class that builds another class. But in the case he’s talking about we’re using a class’ method to build an instance of the same class, which I think is the Builder pattern.
Pattern pedantry aside, I tried putting this advice into practice this week and found it quite pleasing. Here’s some old code. This is from a rake task that kicks off a data processing job. This job can be configured, but in this case we’re using the default configuration.
1 2 3 4 5 6
And here’s the code afterward:
1 2 3 4 5
And there’s a similar task that adds just some students instead of all. Before:
1 2 3 4 5
1 2 3 4 5
Minor changes, but observe how much easier this is to read. In the original examples, what value was there to
new? None, as far as I can tell. All it did was return an instance that had the method I wanted. So if
new has no value in this context, let’s remove it. And we’re left with a declarative statement that says exactly what I want.
In these cases I’m never working with the returned object. But I stuck with Haines’ advice throughout this entire refactoring, using it for creating objects I did work with. Deeper down in the application is the idea of a Queue Event, which we use for tracking when and why data was added to our work queue. Again, this can be configured or there is a default option.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
for may not be the best name here. Something more descriptive would be better.
QueueEvent.for_type, maybe. Or, since there’s a limited number of Event types, I could probably just create explicit builders for all of them:
QueueEvent.registration_update. Etc. Though I can see the maintenance of that being a pain. And the urge to replace these explicit builders with a bit of
method_missing magic is ever present. Though I think it should be resisted. It’s too easy for
method_missing to suprise and confuse other developers (or yourself) months down the road.
After trying out Corey’s advice, I don’t know that I’d stop using
new all-together. Sometimes you just want a new instance of a class. But I can certainly see the value of having explict builders that give you objects in specific states. And the resulting code can be far easier to read.