Как известно, в Smalltalk объекты могут обрабатывать "нестандартые" сообщения - при получении сообщения для обработки которого нет метода будет вызван метод #doesNotUnderstand: (кратко - DNU). Эта способность часто используется для создания разного рода "прозрачных" прокси - для пересылки сообщений удалённым объектам, для ленивой загрузки объектов из БД, или для организации делегирования.
Еще одним вариантом использования перехвата неизвестных сообщений является создание "обучаемых" объектов. Например, объект может перехватывать сообщения с одним параметром, сохранять параметр и, затем, возвращать значение параметра в ответ на соответсвующее унарное сообщение. То есть, при получении сообщения parametr: anObject этот самый anObject будет сохранён в словаре в объекте и будет возвращен в ответ на сообщение parametr.
Класс Teachable является более продвинутой реализацией идеи обучаемых объектов. Его можно обучить отвечать на любое сообщение.
Пример обучения:
|teachable| teachable := Teachable new. teachable whenSend: #help return: 'ok'; whenSend: #doit evaluate: [1 inspect]; acceptSend: #noDebugger; whenSend: #negate: evaluate: [:num | num negated].После обучения объект готов к использованию:
teachable help. вернёт строку 'ok' teachable doit. откроет в инспекторе число 1 teachable noDebugger. Сообщение #noDebugger является допустимым, а его результат - сам обучаемый объект teachable negate: 120 вернёт число -120
Обучаемые объекты можно применять для создания моков для тестов.
Для реализации такого поведения нужно всего 5 методов. Три обучающих:
whenSend: aSymbol evaluate: aBlock При получении сообщения с данным селектором выполнить блок с параметрами полученного сообщения self learnings at: aSymbol put: aBlock whenSend: aSymbol return: anObject В ответ на сообщение с данным селектором вернуть указанный объект self learnings at: aSymbol put: (#return -> anObject) acceptSend: aSymbol Сообщение с данным селектором является допустимым self whenSend: aSymbol return: selfДля ответа на "обученные" сообщения используется DNU:
learnings Возвращает словарь для хранения данных об обучении ^learnings ifNil: [learnings := Dictionary new] doesNotUnderstand: aMessage Если объект не обучен такому сообщению, то будет вызван стандартный метод DNU | learning | learning := self learnings at: aMessage selector ifAbsent:[ ^super doesNotUnderstand: aMessage ]. ^learning class == Association ifTrue: [learning value] ifFalse: [learning valueWithArguments: aMessage arguments]Всё!
Версия для VW находится в публичном Store-репозитории, и состоит из двух пакетов Teachable и Teachable Tests. Версия для Squeak находится в SqueakMap.
Ярлыки: vw