Behavior Patterns Picks: Reversing Method

01 Dec 2014

The Reversing Method pattern (as defined in Smalltalk Best Practice Patterns) is useful to code a smooth flow of messages.

Problem: a Composed Method may not read right because messages are going to too many receivers. You may have a Cascade that doesn't look quite right because several different objects need to receive messages.

A Composed Method divides functionality into smaller methods. A Composed Method might become hard to read if we're sending messages to many receivers.

Consider the following example:

Point>>printOn: aStream
    x printOn: aStream.
    aStream nextPutAll: ' @ '.
    y printOn: aStream

We're sending messages to three different objects; x receives printOn, aStream receives nextPutAll, y receives printOn. You don't have to know much Smalltalk to understand from the code above that our intent is to define a printOn method that will print a formatted Point instance into a stream.

The Reversing Method makes sure that all the messages go through a single object, which improves readability (as we'll see next) and by having the object receive all the messages it can vary without affecting the parameters.

Code a method on the parameter. Derive it's name from the original message. Take the original receiver as the parameter to the new method.

"code a method on the parameter (a 'stream'), derive the name from the original message ('printOn')"
Stream>>print ...

"take the original receiver ('x' and 'y') as the parameter of the new method"
Stream>>print: x
Stream>>print: y

"or more succinctly"
Stream>>print: anObject

Implement the method by sending the original message to the original receiver.

"send the original message ('printOn: aStream') to the original receiver ('x' and 'y', or 'anObject')"
Stream>>print: anObject
    anObject printOn: self

Finally:

Point>>printOn: aStream
    aStream print: x.
    aStream nextPutAll: ' @ '.
    aStream print: y

This is much easier to follow than the example we started with. Smalltalk takes readability one step further with the use of Cascades (which I'll cover on my next post, but here's an example)

Point>>printOn: aStream
    aStream
        print: x;
        nextPutAll: ' @ ';
        print: y

Great code has a rhythm that makes it easy to understand. Code that breaks the rhythm is harder to read and understand. (Kent Beck)