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.