(This page assumes knowledge of the VisitorPattern)
Visitor>visit: anObject | selector | selector := self generateSelector: anObject ifNone: [^self error: 'No visit selector']. ^self perform: selector with: anObject Visitor>generateSelector: anObject ifNone: aBlock | class selector | class := anObject class. [class = nil ifTrue: [^aBlock value]. selector := ('visit' , class name , ':') asSymbol. self respondsTo: selector] whileFalse: [class := class superclass]. ^selectorCreate concrete visitor classes by subclassing Visitor and coding methods such as #visitNumber:, #visitString:, and #visitOrderedCollection:
These options require a little more bookkeeping, but compare for clarity and speed:
Visitor>visit: anObject | class | class := anOject class. class = Integer ifTrue: [^self visitInteger: anObject]. class = Number ifTrue: [^self visitNumber: anObject]. class = String ifTrue: [^self visitString: anObject]. class = OrderedCollection ifTrue: [^self visitOrderedCollection: anObject]. self error: 'Cannot visit ', class printStringor
Visitor>visit: anObject self perform: (self visitSelector: anObject) with: anObject Visitor>visitSelector: anObject ^self visitDictionary at: anObject class ifAbsent: [#visitError:] Visitor>visitError: anObject self error: 'Cannot visit ', anObject class printString Visitor>visitDictionary visitDictionary isNil ifTrue: [visitDictionary := Dictionary new at: Integer put: #visitInteger:; at: Number put: #visitNumber:; at: String put: #visitString:; ...; yourself]. ^visitDictionary
I like 'em (especially #visitError:). They have the progressive flavor of YAGNI and DTST (YouArentGonnaNeedIt and DoTheSimplestThingThatCouldPossiblyWork). If you at some time run into the problem of wanting a visit method to apply to all subclasses, you might (at that time) want to move on to generating the selectors to give you that capability. As in your example, you could dynamically cache the selectors for speed. --StanSilver
!Visitor methodsFor: 'visit'! visit: anObject ^self perform: (self visitSelector: anObject class) with: anObject! visitSelector: aClass ^self selectorDictionary at: aClass ifAbsentPut: [self generateValidSelector: aClass]! generateValidSelector: aClass | class selector | class := aClass. [selector := self generateSelector: class. self respondsTo: selector] whileFalse: [class = Object ifTrue: [^#visitError:]. class := class superclass]. ^selector! generateSelector: aClass ^('visit' , aClass name , ':') asSymbol! selectorDictionary selectorDictionary isNil ifTrue: [selectorDictionary := Dictionary new]. ^selectorDictionary! visitError: anObject self error: 'Cannot visit ' , anObject class printString!
This could also be done in Java, using the Reflection library.