Wednesday, June 9, 2010

My OOP Oh! moment.

Inheritance as the natural control structure in OOL

A few days ago I had my Object Oriented Programming "Oh! moment". I retook my studies of smalltalk reading Ragnar Hojland's LDAPlayer package. I didn't understood its design. Its LDAP-filter category seem to have a class for each possible variation of LDAP filter. A class for each variation of LDAP filter, for god's sake!. The same thing with the LDAP messages or the BER elements. A lot of classes were empty but for a single overloaded method. What a waste!

If I hadn't found Kent Beck's Smalltalk Best Practice Patterns I would remain in ignorance for more time. In the Choosing Message Pattern he says:
Procedural programs implement variations with conditional logic, either if-then-else or case statements. Two problems arise from such hard-coded logic. First, you cannot add a new variation without modifying the logic. This can be a ticklish operation, getting the new variation is without disturbing the existing variations. Second, such logic tends to propagate. You do not have to account for the variation in one place, you have to account for it in several. Adding a new variation means tickling all of the places where the logic lives.

Messages provide a disciplined way to handle theme-and-variation programming. Because the variations live in different objects, they have much less opportunity to interfere with each other than just putting the variations in different parts of the same routine. The client, the object invoking the variations, is also isolated from what variation is currently active.

Adding a new variation is as simple as adding a new object that provides the same set of messages the other variations provide and introducing it to the object that wants to invoke the variations.
If you are choosing between several action according to the parameter's class, then you should convert those actions in a new methods in each one of the respective classes and make an unconditional invocation to the new method. In this way you remove a case statement.

In the same way, if you are choosing between action according to the parameter's value, it is an indication that you should subclass the parameter's class. In this way, an LDAPFilterSingle becomes LDAPFilterWIthNot, LDAPFilterWithAnd and so many others.

The problem with OO teaching is that it makes emphasis in subclases as subtypes and the Liskov substitution principle. With this point of view, your most important work as a designer is to define the correct taxonomy of your objects, and that's one of the most difficult problems that exists. If you are forced to classify all the future types of your system today, you're screwed. Subclasses as subtypes is not the way inheritance was intended to be used. You make a subclass to share common code. You make a subclass to overload a method, so you can avoid an if..then or a case. A subclass is not an ontologic statement about the nature of the instances.

Open Classes goodies

So, subclassing and overloading allows you to reduce the control structures in your methods. But the goodies don't end there. Since in smalltalk all the classes are open (you can add your own methods), this pattern is valid even for classes outside your project. So, the base classes of the system become more powerful with the time.

That's being in another league regards flexibility.

Side note: Ruby has open classes too. As a python programmer I've always seen Ruby as a language too similar to python to be worth to learn it. Now, I feel a little Ruby envy.