Пересылка сообщений в Smalltalk
Поэтому я пишу приложение, в котором у одного объекта есть группа объектов-делегатов, которым он пересылает сообщения. Идея в том, что я могу сказать
someObject sendMessage:aMessage
и aMessage будет отправлено всем делегатам someObject (для любого значения aMessage). Единственный способ, которым я смог сделать это, это что-то вроде:
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].
Это работает, но должен быть лучший способ. Это решение ограничивает аргументы быть строками и выглядит просто ужасно. Кто-нибудь знает более чистый, лучший способ пересылки сообщений?
Кстати, я использую писк, но решение, независимое от реализации, было бы предпочтительным;)
РЕДАКТИРОВАТЬ: я должен добавить, что делегаты того же класса, что и объект, поэтому я не могу просто переопределить DoesNotUnderstand:.
5 ответов
Поскольку вы хотите передавать объекты в качестве аргументов, вам придется передавать их в виде отдельного списка с использованием шаблона сообщения, подобного следующему:
someObject sendMessage: aSelector withArguments: argumentsList
Тогда вы бы реализовали #sendMessage: withArguments: as:
sendMessage: aSelector withArguments: argumentsList
делегаты делают:[:del | del execute: aSelector withArguments::argumentsList].
и вы сможете пересылать произвольно сложные сообщения, используя реальные объекты в качестве аргументов:
| аргументы |
arguments: = Array with: Object new with: 1234.5 with: ('key' -> 'value').
someObject sendMessage: #foo: bar: baz: withArguments: arguments
Я думаю, что это переносимо для большинства диалектов...
В Squeak смотрите класс ObjectTracer. Вы можете использовать его для перехвата всех сообщений, отправляемых Объекту.
Попробуйте реализовать это (он будет только пересылать сообщения, которые не поняты объектом, имеющим делегатов):
doesNotUnderstand: aMessage
delegates
do: [:delegate | aMessage sendTo: delegate]
Вы можете создать объекты Message явно как:
msg := Message selector: #foo arguments: #(bar baz)
"then use them like:"
anObject perform: msg selector with: msg arguments
Ну, не зная, что такое aMessage, и поскольку вы упомянули все ваши объекты делегатов одного класса, я бы сделал что-то вроде:
MyobjectClass>>SendMessage: aMessage
self doSomethingUsefulOnThisInstanceIfApplicable: aMessage.
self dependents do: [:ea | ea SendMessage: aMessage ] .
Вы также можете посмотреть, может ли работать с вами любое из следующих сообщений: (это от Cincom VisualWORKS)
update:
update:with:
update:with:from:
Почему бы просто не использовать полиморфизм, то есть реализовать этот метод в классе каждого объекта, который вы вызываете? Затем вы реализуете в своем объекте метод с тем же именем, который просто делегирует вызов всем подобъектам. Что-то вроде:
MyObjectClass>>someMethod
subobjects do: [:each | each someMethod]