7.4 Template Method

This pattern is massively used, very likely it is present in your code. The principle is to write the structure of a method in an abstract class. The method uses auxiliary methods implemented in sub-classes, each one with a different and specific implementation. In the abstract class, these methods will respond self subclassResponsibility or provide a minimal implementation to be overridden in sub-classes.

In Flyweight, we mentioned briefly the template method pattern in a use case to implement equality between two mathematics items:

DrGMathItem>>= aMathItem
^ aMathItem isMathItem
   and: [self basicType == aMathItem basicType
   and: [self nodeType == aMathItem nodeType
   and: [self parentsEqual: aMathItem] ] ]

Although DrGMathItem provides a simple implementation of parentsEqual:,

parentsEqual: aMathItem
^ parents = aMathItem parents

many of its sub-classes override it to adjust to the mathematics item,

DrGAngleBisector3ptsItem>>parentsEqual: aMathItem
^ parents = aMathItem parents
   or: [parents reverse = aMathItem parents]
DrGPolygonNptsItem>>parentsEqual: aPolygon
|shiftedCollection|
parents size = aPolygon parents size ifFalse: [^ false].
shiftedCollection := parents.
shiftedCollection size timesRepeat: [
   shiftedCollection = aPolygon parents ifTrue: [^ true].
   shiftedCollection := shiftedCollection shiftRight].
shiftedCollection := parents reverse.
shiftedCollection size timesRepeat: [
   shiftedCollection = aPolygon parents ifTrue: [^ true].
   shiftedCollection := shiftedCollection shiftRight].
^  false

In the Builder we have a template method too to decide when to add a mathematics item to a builder:

add: aMathItemCollection at: aPoint
^ (self isWanted: aMathItemCollection at: aPoint)
   and: [
      self addItem: aMathItemCollection at: aPoint.
      "Are we done? If so notify our dependent"
      self readyToBuild ifTrue: [self triggerEvent: #readyToBuild].
      true]

This time the isWanted: and readyToBuild methods are not implemented in the top class of the builder hierarchy:

isWanted: aMathItemCollection
" Check if the builder is interested by aMathItem "
self subclassResponsibility
readyToBuild
" Can the builder build the math item now? "
self subclassResponsibility

Only sub classes implement the behavior to fit their purpose. For example in DrGLocusBuilder:

isWanted: aMathItemCollection
^ aMathItemCollection notEmpty and: [
   (aMathItemCollection first isPointItemOnCurve and: [freePoint isNil])			
   or: [aMathItemCollection first isConstrainedPointItem and: [constrainedPoint isNil]] ]
readyToBuild
^ freePoint notNil and: [constrainedPoint notNil]

buildItem is another template method. It builds an item once enough relevant mathematics items were added to the builder.

buildItem
| itemDefinitions |
itemDefinitions := self mathItemClass.
^ itemDefinitions isCollection 
   ifFalse: [{itemDefinitions newWith: self arguments in: presenter}]
   ifTrue: [point := point + (0.2@1.3).
      itemDefinitions collect: [:class | 
         point := point - (0@0.5).
         class newWith: self arguments in: presenter]]

Its abstract methods mathItemClass and arguments are implemented in the sub classes. Again for locus,

mathItemClass
^ DrGLocus2ptsItem
arguments
^ {freePoint. constrainedPoint}

The builder pattern is very handy to deal with the complexity of constructing various kind of objects, each one with its own constraints. In DrGeo, a builder is associated with tool and state to manage its input and its progress toward creating the new mathematics item. We present tool and state in the next section.