In Smalltalk Best Practice Patterns, Kent Beck describes a pattern called ComposedMethod. The basic idea is that a program should be divided into methods that each perform one identifiable task. Each method should be written so that its operations are all at the same level of abstraction. This is a very good pattern, and well worth using.

It becomes even more important when using inheritance and working in a base class. Consider the following code for drawing line charts (in Smalltalk using the CairoGraphics library):

Chart Drawing Code
ChartView>>renderOn: cr
|transform|
cr
source: ColorValue black;
paint;
source: ColorValue gray.
self xAxis renderVerticalGridIn: self bounds on: cr.
self yAxis renderHorizontalGridIn: self bounds on: cr.
cr source: ColorValue lightGray.
self xAxis renderHorizontalLabelsIn: self bounds on: cr.
self yAxis renderVerticalLabelsIn: self bounds on: cr.
model plots do: [:each |
self xAxis
transformX: cr
bounds: self bounds
while:
[self yAxis
transformY: cr
bounds: self bounds
while: [transform := cr matrix]].
each renderOn: cr withTransform: transform
]

This is a long, procedural method that doesn’t really communicate what it’s doing. Some of the drawing code has been extracted into other objects (like Axes and Plots), but this is still not a very nice method and mixes up levels of abstraction.

What if we wanted a variant of this Chart class that wants to do something differently, like use a different background color, or draw plots differently? The author of this method has left us with only one option: we have to duplicate this method and then change the parts that need to change, leaving us a future maintenance problem.

Instead, we can refactor the code into a ComposedMethod:

Chart Drawing With ComposedMethod
ChartView>>renderOn: cr
self
renderBackgroundOn: cr;
renderAxesOn: cr;
renderAxisLabelsOn: cr;
renderPlotsOn: cr
ChartView>>renderBackgrounOn: cr
cr
source: ColorValue black;
paint
ChartView>>renderAxesOn: cr
cr source: ColorValue gray.
self xAxis renderVerticalGridIn: self bounds on: cr.
self yAxis renderHorizontalGridIn: self bounds on: cr
ChartView>>renderAxisLabelsOn: cr
cr source: ColorValue lightGray.
self xAxis renderHorizontalLabelsIn: self bounds on: cr.
self yAxis renderVerticalLabelsIn: self bounds on: cr
ChartView>>renderPlotsOn: cr
model plots do: [:each | self renderPlot: each on: cr]
ChartView>>renderPlot: aPlot on: cr
self xAxis
transformX: cr
bounds: self bounds
while:
[self yAxis
transformY: cr
bounds: self bounds
while: [transform := cr matrix]].
aPlot renderOn: cr withTransform: transform

Not only is this version more readable and understandable, it is easier to customize with subclasses. The more pieces we break our methods into, the more “hooks” we provide to subclasses for customizing just the right part of the behavior.

It might even be desirable to break things up a little bit more: setting the source on a CairoContext is a different level of abstraction than telling an object to render itself:

Even More ComposedMethod
ChartView>>renderOn: cr
self
renderBackgroundOn: cr;
renderAxesOn: cr;
renderAxisLabelsOn: cr;
renderPlotsOn: cr
ChartView>>renderBackgrounOn: cr
self useBackgroundColorOn: cr.
cr paint
ChartView>>renderAxesOn: cr
self useAxisColorOn: cr.
self xAxis renderVerticalGridIn: self bounds on: cr.
self yAxis renderHorizontalGridIn: self bounds on: cr
ChartView>>renderAxisLabelsOn: cr
self useLabelColorOn: cr.
self xAxis renderHorizontalLabelsIn: self bounds on: cr.
self yAxis renderVerticalLabelsIn: self bounds on: cr
ChartView>>renderPlotsOn: cr
model plots do: [:each | self renderPlot: each on: cr]
ChartView>>renderPlot: aPlot on: cr
self xAxis
transformX: cr
bounds: self bounds
while:
[self yAxis
transformY: cr
bounds: self bounds
while: [transform := cr matrix]].
aPlot renderOn: cr withTransform: transform
ChartView>>useBackgroundColorOn: cr
cr source: ColorValue black
ChartView>>useAxisColorOn: cr
cr source: ColorValue gray
ChartView>>useLabelColorOn: cr
cr source: ColorValue lightGray

I might not always break things down this much, but this variant does allow easy customization of the various colors by subclasses.

Inheritance is not the first tool I reach for when designing, but when I need it, I try to make it easy for subclasses to do what they need to do. ComposedMethod is one way to do that.

By the way, you really should read Smalltalk Best Practice Patterns, even if you’re not a Smalltalk programmer. It will teach you a lot about good low-level design. You can listen to the Ruby Rogues book club episode about the book if you want more information first.