Essentially, the idea is that you have a method:
#addInteger: which knows how to add integers,#addFloat: which knows how to add float,- etc.
Now in the Integer class you define + as:
+ otherObject otherObject addInteger: self
in Float you define it as:
+ otherObject otherObject addFloat: self
Thus, you only need to send + to the object, and then it will ask the receiver to add it using the required method.
Another strategy is to use #adaptTo:andSend: methods. For example, the + in Point class is defined as:
+ arg arg isPoint ifTrue: [^ (x + arg x) @ (y + arg y)]. ^ arg adaptToPoint: self andSend: #+
First, it is checked whether the argument is a point, and if not, it requests an argument to adapt to the point and send some character (operation), this saves some duplication of methods that must perform a slightly different operation.
Collection implements the method as follows:
adaptToPoint: rcvr andSend: selector ^ self collect: [:element | rcvr perform: selector with: element]
and Number implements it as follows:
adaptToPoint: rcvr andSend: selector ^ rcvr perform: selector with: self@self
Note: to avoid explicit type checking, we could define this method in Point as follows:
adaptToPoint: rcvr andSend: selector ^ (x perform: selector with: arg x) @ (y perform: selector with: arg y)
You can see more examples in this presentation: http://www.slideshare.net/SmalltalkWorld/stoop-302double-dispatch