Главная страница

дб. Четвертое издание джозеф Джарратано Университет Хьюстон клиэрЛэйк Гари Райли People5oft, Издательский дом "Вильямс" Москва СанктПетербург Киев 2007 ббк 32. 973. 26 018 75 Д


Скачать 3.73 Mb.
НазваниеЧетвертое издание джозеф Джарратано Университет Хьюстон клиэрЛэйк Гари Райли People5oft, Издательский дом "Вильямс" Москва СанктПетербург Киев 2007 ббк 32. 973. 26 018 75 Д
Дата19.05.2022
Размер3.73 Mb.
Формат файлаpdf
Имя файла[Dzharratano Dzhozef, Raili Gar - Nieizviestnyi.pdf
ТипДокументы
#538649
страница68 из 74
1   ...   64   65   66   67   68   69   70   71   ...   74

(defmessage-handler FOREIGN-ORDER put-order-price around (?
value) (override-next-handler (/ ?value ?self:exchange-rate))) Класс — ORDER имеет слот exchange — rate, который используется для представления валютного курса,
связывающего иностранную валюту и доллары США. Например,
если валютный курс равен 2, то 10 долларов США эквивалентны единицам иностранной валюты. Для использования данного подхода на практике, по-видимому, потребовалось бы предусмотреть еще один слот, который указывал бы название единицы иностранной валюты (например, евро, но для данного примера в этом нет необходимости. В обработчике get-order —
price типа around класса FOREIGN — ORDER вызывается обработчик get — order — price типа primary класса ORDER с помощью команды call — next — handler. Затем возвращаемое значение умножается назначение слота exchange — rate и происходит возврат полученного значения. В обработчике put —
order — price типа around класса FORE IGN — вызывается обработчик put — order — price типа primary класса с помощью команды override — next — handler, но вместо передачи значения, соответствующего стоимости в иностранной валюте, это значение делится на валютный курс и вместо него передается полученное значение, представленное в долларах США. Мы можем понаблюдать за действиями,
выполняемыми в классе FOREIGN- CURRENCY, отслеживая
работу обработчиков сообщений. Вначале создадим экземпляр класса CLIPS> (unlatch all)J CLIPS>
11.10. Обработчики сообщений befoãå, ай

ег и around 905
(make-instance order6 of FOREIGN-ORDER (ID 4006) (ezchange- rate 2) (sales-taz . 10) ) 1 [order 6] CLIPS> Затем изменим значение слота order — price, введя вместо него значение 20. в иностранной валюте CLIPS> (watch message-handlers)J
CLIPS> (send [order6] put-order-price 20.00)J HND » put-order- price around in class FOREIGN-ORDER ED:1 (
20.0) HND » put-order-price primary in class ORDER ED:1
( 10.0) HND « put-order-price primary in class
ORDER ED:1 ( 10.0) HND « put-order-price around in class FOREIGN-ORDER ED:1 ( 20.0)
10.0 CLIPS> Прежде всего вызывается обработчик put-order —
price типа around класса FOREIGN-ORDER со значением 2 О.
После этого вызывается обработчик putorder-price типа primary класса ORDER со значением 10, полученным в результате вызова команды override-next — handler. Затем указанное значение в долларах США передается назад в качестве возвращаемого значения с помощью команды send. Операция выборки значения слота order — price осуществляется аналогичным образом CLIPS> (send [огбегб] get-order-price)J
HND » get-order-price around in class FOREIGN-ORDER ED:1
() HND » get-order-price primary in class ORDER
ED:1 () HND « get-order-price primary in class
ORDER ED:1 () HND « get-order-price around in class FOREIGN-ORDER ED:1 () 20.0 Глава 11. Классы, экземпляры и обработчики сообщений Вначале вызывается обработчик get — order-price типа around класса FOREIGN — ORDER, который вызывает обработчик get
— order — price типа primary класса ORDER с помощью команды call — next — handler. Этот обработчик типа primary возвращает
значение 10 в долларах США, которое затем умножается на валютный курс в обработчике типа around для получения окончательного возвращаемого значения в единицах иностранной валюты, равного 20. Передача сообщения print экземпляру FOREIGN — CURRENCY показывает, что значение стоимости в иностранной валюте фактически хранится в виде суммы в долларах США, а не в виде суммы в иностранной валюте CLIPS> (unwatch all)J CLIPS> (send [огбегб) print)J
[огбегб] of FOREIGN — ORDER (ID №006) (total-price 0.0) (order- price 10.0) (sales-tax 0.1) (exchange-rate 2) CLIPS> Порядок вызова обработчиков на выполнение Выше былы описаны четыре метода модификации действий, выполняемых классом путем закрепления обработчиков за классом MY —
ORDER: перекрытие обработчика primary, определение обработчика before, определение обработчика after, а также определение обработчиков after и around. Теперь необходимо найти ответ на очевидный вопрос какой подход является наилучшим В данном случае, по-видимому, наилучшим решением является использование обработчика before. В этом подходе применяется наименьший объем кодак тому же он предоставляет одно существенное преимущество по сравнению стем, когда осуществляется перекрытие обработчика последний характеризуется использованием количества кода,
находящегося на втором месте после наименьшего в нем вызываются все унаследованные обработчики сообщений типа before и after, принадлежащие к классу или его суперклассам,
если из-за ошибки, возникшей в обработчике around, не прекращается обработка сообщения. Это означает, что в классе можно лишь слегка модифицировать действия, выполняемые суперклассом, с использованием обработчиков типа before и after, не перекрывая обработчик типа primary, а в подклассах невозможно предотвратить вызов на выполнение обработчика типа before или типа after, если только в них не прекращена обработка сообщения, что влечет за собой прекращение выполнения всех обработчиков типа before, af ter

11.10. Обработчики сообщений before, after и around 907 и primary. Если действия, выполняемые существующим классом,
модифицированы путем переопределения их в новом классе, а затем перекрыт обработчик типа primary, тоновый обработчик типа primary также будет подвержен перекрытию в подклассе.
Если в перекрывающем классе не вызывается функция call —
next — handler, тоне будет вызвана на выполнение и новая версия обработчика типа primary. Тем самым мы не утверждаем,
что не следует никогда перекрывать обработчик типа primary, но если вы задумали какие-то специальные действия, возможность перекрытия которых в классах, наследующих свойства,
нежелательна, то определенно следует подумать о возможности размещения кода реализации этих действий в обработчике типа before или after. Предположим, что принято решение ввести код реализации каких-либо специализированных действий в несколько обработчиков типа before, after и унаследованных классом. В таком случае важно знать, в каком порядке вызываются различные обработчики сообщений. После передачи экземпляру какого-либо сообщения с помощью команды send определяются все применимые обработчики сообщений и выполняются описанные ниже шаги. 1. Если есть какие-либо не вызванные обработчики типа around, то вызвать наиболее конкретный из них в противном случае перейти к шагу. Если в вызванном обработчике типа around вызывается функция call — next — handler или override — next — handler, то повторить данный шаг в противном случае перейти к шагу 6. Если есть какие-либо не вызванные обработчики типа before, то вызвать наиболее конкретный обработчик, дождаться завершения его работы, а затем повторить данный шаг. В
противном случае перейти к шагу 3. Возвращаемые значения обработчиков before игнорируются. Не предусмотрено никакого способа непосредственно возвратить эти значения в другой обработчик. 3. Если есть какие-нибудь не вызванные обработчики primary, то вызвать наиболее конкретный обработчик в противном случае перейти к шагу 4. Если вызванный обработчик primary вызывает обработчик call — next
— handler или override — next — handler, то повторить данный
шаг в противном случае перейти к шагу 4. Экземпляру не может быть передано сообщение, если отсутствует по меньшей мере один применимый обработчик primary. 4. Предоставить возможность каждому обработчику primary завершить свою работу и выполнить возврата затем удалить его из списка вызванных обработчиков primary. Если обработчик primary снова вызывает обработчик call — next — handler или override — next
— handler, возвратиться к шагу 3. После завершения работы всех обработчиков primary записать Глава 11. Классы, экземпляры и обработчики сообщений в память значение, возвращенное последним выполненным обработчиком primary, и перейти к шагу 5. 5. Если есть какие- либо не вызванные обработчики a f tåã, то вызвать наименее конкретный обработчик, дождаться завершения его работы, а затем повторить данный шаг. В противном случае перейти к шагу 6. Возвращаемые значения обработчиков after игнорируются. Не предусмотрено никакого способа непосредственно возвратить эти значения в другой обработчик. Если не вызванные обработчики around отсутствуют, перейти к шагу 7. В противном случае предоставить возможность каждому обработчику around завершить свою работу и выполнить возврата затем удалить его из списка вызванных обработчиков around. Если какой-либо обработчик around снова вызывает обработчик call — next — handler или override — next
— handler, вернуться к шагу 1. После завершения работы всех обработчиков around записать в память значение, возвращенное последним выполненным обработчиком around и перейти к шагу. 7. Окончательным возвращаемым значением команды send становится возвращаемое значение самого конкретного обработчика around, полученное на шаге 6. Если обработчики around отсутствуют, то используется возвращаемое значение самого конкретного обработчика primary, полученное на шаге Получение возвращаемого значения функции call — next —
handler или override — next — handler — это последнее
действие, выполняемое в следующем вызванном обработчике around или primary. Обработчик может либо игнорировать это значение, либо использовать его в качестве возвращаемого значения. На рис. 11.2 приведена блок-схема, позволяющая получить такие же результаты, как и при выполнении шагов 1 —
7. Шаг 1 начинается с прямоугольника в верхнем левом углу этого рисунка. А на рис. 11.3 показана блок-схема, позволяющая получить результаты, эквивалентные результатам шагов 3 и касающиеся выполнения применимых обработчиков типа primary, которые связаны с сообщением, переданным экземпляру. В тех случаях, когда отсутствуют применимые обработчики типа around, be йоге или ай1ег (те. имеются только обработчики типа primary), блок-схема, приведенная на рис, сокращается и становится эквивалентной блок-схеме на рис. 11.3. Для демонстрации порядка вызова обработчиков на выполнение будут использоваться следующие конструкции A (is — а USER)) (defmessage-handler А msg1 primary ()
(return А) (defmessage-handler А msg1 before ())
11.10. Обработчики сообщений before, after и around 909 Да
Да Нет Нет Да Нет Нет Нет Нет Нет Да Нет Рис. Определение порядка вызова обработчиков на выполнение
Определить все обработчики сообщений, применимые для сообщения, переданного экземпляру Имеется ли какой-либо невызванный обработчик around? Передать возвращаемое значение обработчика primary обратно обработчикуагоипЫ с помощью вызова нкции call-next-handler Вызывает ли выполняющийся обработчик функцию call-next-handled? Дать возможность текущему обработчику around закончить свою работу и возвратить значение. Рассматривать этот обработчик как невызванный
Глава 11. Классы, экземпляры и обработчики сообщений Нет Рис. 11.3. Определение порядка вызова на выполнение
обработчиков типа primary (defmessage-handler А msg1 after ())
(defmessage-handler А msg1 around () (call-next-handler)) (В. Обработчики сообщений before, after и around 911 (а А В msg1 primary () (return В В msg1 before ()) (defmessage-handler В after ()) (defmessage-handler В msgl around () (call-next- handler)) Класс В является подклассом класса А. Каждый класс имеет свои собственные обработчики primary, before, after и around. Вначале создадим экземпляры обоих классов CLIPS>
(make-instance а of A)J а CLIPS> (make-instance [b1] of B) J
[b1] CLIPS> После этого организуем отслеживание работы обработчиков сообщений командой watch с помощью ключевого слова message — handlers и передадим экземпляру al сообщение msgl: CLIPS> (watch message-handlers)J CLIPS>
(send а msg1)J HND » msg1 around in class А ED:1 () HND » msg1 before in class А ED:1 () HND «
msg1 before in class A ED:1 () HND » msgl primary in class А ED:1 () HND « msg1 primary in class А ED:1
() HND » msg1 after in class А ED:1 ()
HND « msg1 after in class А ED:1 () HND « msg1
around in class А ED:1 () А CLIPS>
912 Глава 11. Классы, экземпляры и обработчики сообщений
Для этого сообщения есть четыре применимых обработчика сообщений обработчики msgl типа primary, before, after и around класса А. Начиная с шага 1 вызывается на выполнение обработчик msgl типа around класса A. В этом обработчике вызывается функция call — next — handler, поэтому шаг повторяется. Поскольку нет других обработчиков типа оставшихся не вызванными, переходим к шагу 2. Обработчику msgl типа before класса A разрешается выполнить свой код и возвратить управление. Другие не вызванные обработчики типа
before отсутствуют, поэтому переходим к шагу 3. На выполнение вызывается обработчик сообщений msgl типа primary класса В нем не вызывается функция call-next — handler, поэтому переходим к шагу 4. Обработчику сообщений msgl типа primary класса A разрешается завершить свое выполнение, после чего он возвращает символ msgl — А. На этом выполнение всех обработчиков типа primary завершается, поэтому переходим к шагу 5. Теперь разрешается выполнить свой код и возвратить управление обработчику тяд1 типа after класса А. Какие-либо иные не вызванные обработчики типа after отсутствуют, поэтому переходим к шагу 6. Обработчику msgl типа around класса разрешается закончить свою работу. Последним действием,
выполняемым этим обработчиком, является вызов функции call
— next — handler, поэтому возвращаемым значением становится возвращаемое значение следующего вызванного обработчика типа around или primary. В данном случае возвращаемое значение А должен был иметь обработчик msgl типа primary класса А, поэтому возвращаемое значение обработчика типа around остается тем же самым. На этом работа всех обработчиков типа around заканчивается, поэтому переходим к шагу 7. Окончательным возвращаемым значением команды send становится значение msgl — A, возвращенное обработчиком msgl типа around класса А. Теперь передадим сообщение msgl экземпляру bl следующим образом CLIPS>
(send [bi) msg1)J HND » msgl around in class В ED:1 () HND » msgl around in class А ED:1 () HND » msgl before in class В ED:1 () HND « msgl before in class В () HND » msgl before in class А ED:1 () HND « msgl before in class А ED:1 ()
11.10. Обработчики сообщений before, after и around 913 HND »
msg1 primary in class В ED:1 () HND « msg1 primary in class В ED:1 () HND » тяд1 after in class A ED:1
() HND « msg1 after in class А ED:1 ()
HND » msgl after in class В ED:1 () HND « msg1 after
in class В ED:1 () HND « msg1 around in class А () HND « msg1 around in class В ED:1
() тяд1-В CLIPS> Для данного сообщения имеются восемь применимых обработчиков сообщений обработчики msg1 типа primary, before, after и around класса A и обработчики msg1 типа primary before, after и around класса В. Начиная с шага 1 выполняется обработчик msg1 типа around класса В,
поскольку он является наиболее конкретным обработчиком around. В этом обработчике вызывается функция call — next —
handler, поэтому шаг 1 повторяется. Наследующем месте среди наиболее конкретных обработчиков around находится обработчик msg1 типа around класса A. В этом обработчике также вызывается функция call — next — handler, поэтому шаг повторяется еще один раз. Какие-либо оставшиеся не вызванными обработчики around отсутствуют, поэтому переходим к шагу 2. Наиболее конкретным обработчиком типа before является обработчик msg1 типа before класса В, поэтому он вызывается на выполнение в первую очередь, после чего разрешается завершить его работу. Наследующем месте среди наиболее конкретных обработчиков этого типа находится обработчик msg1 типа before класса А, поэтому разрешается его выполнение и возврат управления. Какие-либо оставшиеся не вызванными обработчики типа before отсутствуют, поэтому переходим к шагу 3. На выполнение вызывается обработчик сообщений msg1 типа primary класса В, так как он является наиболее конкретным обработчиком типа primary. В нем не вызывается функция call — next-handler, поэтому обработчик msg1 типа primary класса Ане выполняется и осуществляется переход к шагу 4. Разрешается завершение работы обработчика сообщений msg1 типа primary класса В, ион возвращает Глава 11. Классы, экземпляры и обработчики сообщений символ msgl — В. На этом выполнение всех обработчиков типа primary заканчивается, поэтому переходим к шагу 5. В порядке,
противоположном запуску обработчиков типа before, вначале
разрешается выполнение обработчика msgl типа аЙ1ег класса Аи выполняется его возврат, поскольку он является наименее конкретным обработчиком типа after. Затем разрешается выполнение и возврат обработчика msgl типа after класса В.
Поскольку не остается больше каких-либо других не вызванных обработчиков типа айег, переходим к шагу 6. Обработчику msgl типа around класса А разрешается завершить свою работу.
Последним действием, выполняемым этим обработчиком,
становится вызов функции call — next — handler, поэтому возвращаемым значением становится значение следующего обработчика around или primary, который был вызван. В данном случае это был обработчик msgl типа primary класса В, который имел возвращаемое значение msgl — 8, поэтому возвращаемое значение данного обработчика around становится тем же самым.
Теперь разрешается завершить свою работу обработчику ms типа around класса В. Его последним действием также был вызов функции call — next — handler, поэтому его возвращаемым значением становится возвращаемое значение обработчика msgl типа around класса А, которое представляет собой msgl — В. Поскольку все обработчики типа around завершили свою работу, переходим к шагу 7. Окончательным возвращаемым значением команды send становится значение В, возвращенное обработчиком msgl типа around класса Для вывода на внешнее устройство списка обработчиков,
применимых для обработки определенного сообщения,
переданного экземпляру указанного класса, может использоваться команда preview-send. Она имеет следующий синтаксис (preview-send Например, вместо отслеживания работы обработчиков сообщений с помощью ключевого слова message-handlers и передачи сообщения msgl в экземпляр bl можно было бы воспользоваться следующей командой CLIPS> (preview-send В » msgl around in class В » msgl around in class А » msgl before in class В « msgl before in class В » msgl before in class А «
msgl before in class А » msgl primary in class В » msgl primary in class А « msgl primary in class А « msgl primary in class В 1 1 »
msgl after in class А

915 11.11. Создание экземпляра, инициализация и удаление. «
msg1 after in class А » msg1 after in class В « msg1 after in class В msg1 around in class А « msg1 around in class В Приведенные выше результаты показывают, что все применимые обработчики перечислены в том порядке, в каком они были бы вызваны, если бы в каждом обработчике типа around и primary вызывалась функция call — next — Уровень отступа обозначает вложенность вызовов обработчиков. По аналогии стем вариантом, когда осуществляется отслеживание работы обработчиков сообщений с помощью ключевого слова message — handlers, символы » и
«обозначают начало и конец выполнения кода обработчика.
Вертикальные ряды знаков связывают начальную и конечную части выполнения обработчиков, которые должны вызывать функцию call — next — handler для обеспечения выполнения обработчиков суперклассов. Обычно удобнее вызывать команду preview — send, чем использовать команду watch message —
handlers и действительно передавать экземпляру сообщение.
Первый способ позволяет проще ознакомиться со списком применимых обработчиков для данного сообщения и класса экземпляра. Тем не менее первый способ показывает, что могло быть вызвано, а второй способ позволяет ознакомиться с тем,
что действительно было вызвано. 11.11 Создание экземпляра,
инициализация и удаление обработчиков сообщений Как уже было сказано выше в данной главе, в языке предусмотрено несколько заранее определенных обработчиков сообщений, которые могут быть унаследованы от класса в том числе обработчики сообщений create, init и Обработчик сообщений create вызывается после создания экземпляра, но перед применением каких-либо предусмотренных по умолчанию значений или перекрытых значений слотов. Обработчик сообщений init вызывается после выполнения операций перекрытия значений слотов для задания всех оставшихся значений слотов, которые небыли перекрыты своими значениями, заданными по умолчанию. А обработчик
сообщений delete либо вызывается явно для удаления экземпляра, либо вызывается автоматически при вызове функции make — instance и указании в качестве имени экземпляра имени уже существующего экземпляра. Вообще говоря, при определении собственных классов не следует перекрывать обработчик типа primary, входящий в число указанных обработчиков сообщений, определенных в классе, но может оказаться весь Глава 11. Классы, экземпляры и обработчики сообщений ма полезной возможность определить собственные обработчики типа before и айег, которые отвечали бы на существующие сообщения. Вначале рассмотрим ситуацию, в которой может потребоваться определить обработчик init типа Предположим, что имеется следующая конструкция defclass:
(defclass PERSON (is — а USER) (slot пате (type STRING)
(access initialize-only)) (slot middle-name (type STRING) (access initialize-only)) (slot пате (type STRING) (access initialize-only))
(slot пате (type STRING) (access initialize-only))) При определении этой конструкции defclass с именем предусмотрено такое ограничение, что имя лица задается при создании экземпляра PERSON ив дальнейшем не изменяется.
Слот full — пате предназначен для хранения результатов конкатенации значений слотов f irst — name, middle-name и last
— пате с включением пробела между каждым компонентом полного имени. Итак, слот пате может быть задан только вовремя инициализации, поэтому необходимо предоставлять для него значение наряду со значениями других компонентов полного имени, чтобы этот слот был заполнен правильно, как в следующем примере CLIPS> (make-instance р of паве "John" ) (паве "Quincy") (last-name "Public" ) (full- name "John Quincy Public"))J [pl] CLIPS> Тем не менее можно избавиться от необходимости задавать значение слота full-пате,
предусмотрев использование обработчика сообщений init типа ай1ег, который автоматически создает значение полного имени
из значений других слотов, как показано ниже. (defmessage- handler PERSON init after () (bind пате (str-cat ?self:first- пате. Создание экземпляра, инициализация и удаление. 917 пате ?self:last-name))) Отслеживая работу соответствующих обработчиков сообщений in i t, можно видеть,
что обработчик сообщений init типа аЙ1ег с именем вызывается после обработчика сообщений init с именем USER и значение, сохраняемое в слоте full — пате, принимает требуемый вид CLIPS> (watch message-handlers USER init)J
CLIPS> (watch message-handlers PERSON init)J CLIPS> (make- instance [p2] of PERSON (first-name "Jane" ) (паве "Paula" )
(last-name "Public" )) HND » init primary in class USER ED:1
() HND « init primary in class USER ED:1 () HND » init after in class PERSON ED:1 () HND «
init after in class PERSON ED:1 () [P>] CLIPS> (р get-full-name)J "Jane Paula Public" CLIPS> Следует отметить,
что обычно не имеет особого смысла определять обработчик init типа before, поскольку ко времени его вызова все значения слотов экземпляра все еще остаются неинициализированными.
Атрибут storage При изучении примера применения обработчика create типа after и обработчика delete типа before воспользуемся атрибутом слота storage. Если этому атрибуту присвоено значение local, которое является значением, предусмотренным по умолчанию, то каждый создаваемый экземпляр получает свою собственную область памяти, предназначенную для хранения значения слота. Если атрибуту storage присваивается значение shared, то все экземпляры данного класса совместно используют область памяти, предусмотренную для значения слота. Если значение слота изменяется для одного экземпляра,
то данное изменение применяется во всех экземплярах.
Например, ниже приведено объявление

918 Глава 11. Классы, экземпляры и обработчики сообщений класса, в котором содержится слот count с атрибутом имеющим значение а USER) (slot count (storage shared) (default 1))) Если будут созданы два экземпляра, а затем изменено значение слота count водном экземпляре, то, как показывает следующая последовательность команд, значение слота count изменится ив другом экземпляре CLIPS> (make-instance i1 of INSTANCE-
COUNTER)J [il] CLIPS> (make-instance з of INSTANCE-
COUNTER)J [i2] CLIPS> (send [i1] get-count)J 1 CLIPS> (send [i2]
get-count)J 1 CLIPS> (send [i1] put-count 2)J 2 CLIPS> (send [i1]
get-count)J 2 CLIPS> (send [i1] get-count)J 2 CLIPS> С помощью такого средства можно создать обработчики сообщений create типа after и delete типа before, которые будут увеличивать значение слота count после создания каждого экземпляра данного класса и уменьшать значение слота count после уничтожения каждого экземпляра этого класса (defmessage- handler INSTANCE-COUNTER create after () (if (integerp ?
self:count) then (send ?self put-count (+ ?self:count 1))))

1   ...   64   65   66   67   68   69   70   71   ...   74


написать администратору сайта