In The ThoughtWorks Anthology there is a chapter called "Object Calisthenics". This section lays out a challenge to help push your understanding of object-oriented programming concepts. I decided to take this challenge by building a little Flex application, and I've been quite surprised by its effect. It really does make you think very deeply about what OOP means and how it affects the way you program.
In essence, the challenge is to write a non-trivial (about 1000 lines) program of your choosing that does not violate a set of 9 rules. The rules are very draconian, and they aren't advocating that you actually write all of your software this way (though obeying them in most cases probably would be a good thing). But the goal of the exercise is to really expose any lingering influence of procedural coding and force you to come to terms with them. While I chose to do this in Flex and ActionScript (since that is what I am currently learning in depth), I think some of these would probably need a bit of tweaking in a ColdFusion implementation, but most of it would still apply. Here are the rules:
- One level of indentation per method. If you need more than one level in from the start of the method body, create another method and call it. So one level of a loop or if statement is ok, but any deeper and you need to break it out into its own method.
- Don't use the ELSE keyword. This one is tough. We're very used to using if/else or switch/case. But good OO designs rely on polymorphism in place of conditional logic.
- Wrap all primitives and Strings. That means instead of var zipCode : String, you need var zipCode : ZipCode, and instead of var age : int, you need var age : Age. The idea is to ensure that everything is an object, that the purpose of everything is self-evident from it's type, and that behavior related to that object has somewhere to go.
- Use first class collections. This means you can't have var cartItems : ArrayList, but instead have var cartItems : CartItems. This means that behavior related to the collection has a place to live, and that the collection should contain no other instance variables.
- One dot per line. This is meant to enforce the Law of Demeter. So this would be a no-no: invoice.lineItems.getLineItem(4). Although this discourages method-chaining in cases where a method returns the same object (a la JQuery), that isn't what this rule is trying to do. It's trying to stop you from reaching across class boundaries and digging into the guts of other objects.
- Don't abbreviate anything. This is meant to enforce clarity, as well as identify duplication or misplaced responsibilities. If you're typing mergeUserPreferencesFromDatabaseAndCookies() too often, something is probably wrong, both in terms of what the method is doing and how many things are coupled to it.
- Keep entities small. No class over 50 lines, and no package over 10 files.
- No classes with more than two instance variables. This is meant to ruthlessly enforce the single responsibility principle for objects. If you need more instance variables, break them into composed objects.
- No getter, setter, and property calls to other objects. This mandates the principle to "Tell, don't ask" and enforces strong encapsulation boundaries.
I chose to write a program that generates a game of 10 pin bowling, and then scores it. And wow, writing code to score a game of bowling was actually a lot harder than I initially thought, even without the above rules. But I'm just about done with it and when I am I'll post it here in case anyone wants to take a look at it.
But before I post it, I thought I would ask if anyone else is interested in giving this a shot? Would anyone be up for trying this (with a bowling score card or any other idea you like), sending them to me, and having me post them (anonymously if people prefer) and start a blog discussion about them? I think it would be quite educational to see how different people approach these rules to solve problems, and I'm sure everyone involved would benefit. Any takers?