4

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:.

Alex
  • 4,316
  • 2
  • 24
  • 28

5 Answers5

7

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...

Dale Henrichs
  • 1,293
  • 9
  • 16
2

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
John Cromartie
  • 4,184
  • 27
  • 32
  • Thanks for the suggestion, but the object is of the same type as the delegates, so I can't do that. I added that to the original question. – Alex May 19 '09 at 19:09
  • Perhaps you could create a new class for this delegate collection and then use doesNotUnderstand? Without doing more explicit message creation it's probably the most straightforward way. – John Cromartie May 19 '09 at 20:18
2

In Squeak, see the class ObjectTracer. You can use it to intercept all message sends to an Object.

Randal Schwartz
  • 39,428
  • 4
  • 43
  • 70
1

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:
Curtis
  • 1,189
  • 2
  • 11
  • 22
0

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]
Janko Mivšek
  • 3,954
  • 3
  • 23
  • 29