So I'm writing an application where one object has a bunch of delegate objects that it forwards messages to. The idea is that I can say
someObject sendMessage:aMessage
and aMessage will be sent to all of someObject's delegates (for any value of aMessage). The only way I've been able to do this is something like:
sendMessage:aMessage
| sel chunks kwords arglist msg |
chunks := aMessage findTokens:' '.
kwords := Array new:(chunks size).
arglist := Array new:(chunks size).
1 to: (chunks size) do: [:i |
kwords at:i put:((chunks at:i) findTokens:':') at:1.
arglist at:i put:((chunks at:i) findTokens:':') at:2].
sel := ''.
kwords do:[:word | sel := sel,word,':'].
msg := Message selector:sel arguments:arglist.
delegates do:[:del | del perform:msg selector with:msg arguments].
It works, but there has to be a better way. This solution limits the arguments to being strings, and is just plain ugly. Does anyone know a cleaner, better way to forward messages?
BTW, I'm using squeak, but an implementation-independent solution would be prefered ;)
EDIT: I should add that the delegates are of the same class as the object, so I can't just override DoesNotUnderstand:.
Since you want to pass objects in as arguments, you'll have to pass them in as a separate list of using a message pattern like the following:
someObject sendMessage: aSelector withArguments: argumentList
Then you'd implement #sendMessage:withArguments: as:
sendMessage: aSelector withArguments: argumentList
delegates do:[:del |
del perform: aSelector withArguments: :argumentList].
and you'd be able to forward arbitrarily complex messages using real objects as args:
| arguments |
arguments := Array
with: Object new
with: 1234.5
with: ('key'->'value').
someObject sendMessage: #foo:bar:baz: withArguments: arguments
I think this is portable to most dialects as well...
Try implementing this (it will only forward messages that aren't understood by the object that has the delegates):
doesNotUnderstand: aMessage
delegates
do: [:delegate | aMessage sendTo: delegate]
You could construct Message objects explicitly like:
msg := Message selector: #foo arguments: #(bar baz)
"then use them like:"
anObject perform: msg selector with: msg arguments
In Squeak, see the class ObjectTracer. You can use it to intercept all message sends to an Object.
Well, without knowing what aMessage is, and since you mentioned all your delegate objects are of the same class, I'd do something like:
MyobjectClass>>SendMessage: aMessage
self doSomethingUsefulOnThisInstanceIfApplicable: aMessage.
self dependents do: [:ea | ea SendMessage: aMessage ] .
You may also want to look to see if using any of the following messages could work for you: (these are from Cincom VisualWORKS)
update:
update:with:
update:with:from:
Why not simply use a polymorphism, that is, implement this method in class of each object you are calling? Then you implement in your object method with the same name, which just delegates a call to all subobjects. Something like:
MyObjectClass>>someMethod
subobjects do: [:each | each someMethod]