7.2.1.
Передача сообщений
Идея
объектно-ориентированного программирования состоит в том, что программа строится
вокруг множества объектов, каждый из которых обладает собственным набором функций
(операций). Вместо того чтобы представлять объект пассивным набором данных, объектно-ориентированная
система позволяет объекту играть более активную роль, в частности взаимодействовать
с другими объектами, обмениваясь с ними сообщениями. В результате основной упор
переносится с разработки общей управляющей структуры программы, которая ответственна
за порядок обращения к функциям, на конструирование самих объектов, выяснение
их ролей и создание протоколов взаимодействия объектов. Эти протоколы, по существу,
определяют интерфейс между объектами. Если один объект должен взаимодействовать
с другим, он должен вызывать функции в строгом соответствии с этим интерфейсом.
Объекты
располагают собственными данными, которые играют ту же роль, что и слоты фреймов,
собственным механизмом обновления этих данных и использования хранящейся в них
информации. Помимо функций интерфейса, объекты располагают собственными, "приватными"
функциями, которые, как правило, представляют собой реализацию определенной родовой
операции применительно к данному объекту. Помимо данных, передаваемых в качестве
аргументов родовой операции, такие функции используют и локальные данные объекта
— аналоги слотов фрейма.
Предположим,
мы определили объект, представляющий класс ship (корабль), и наделили его свойствами
x-velocity (скорость по х) и y-velocity (скорость по у). Теперь
можно создать экземпляр класса ship, назвать его Titanic и одновременно присвоить
свойствам x-velocity и y-velocity нового экземпляра исходные значения. Практически
нет никаких отличий между этой процедурой и процедурой создания нового экземпляра
фрейма, рассмотренной в предыдущей главе.
Предположим
теперь, что нам понадобилось определить процедуру speed, которая будет вычислять
скорость судна на основании значений свойств x-velocity и у-velocity (скорость
вычисляется как корень квадратный из суммы квадратов компонентов). Такая процедура
будет принадлежать абстрактному типу данных, представляющему любые суда (в терминологии
языка SmallTalk speed — это метод класса ships, а в терминологии C++ — функция-член
класса ships.)
Идея
состоит в том, чтобы закодировать в объекте (классе) не только декларативные знания
о судах, но и процедурные, т.е. методы использования декларативных знаний. Для
того чтобы активизировать процедуру вычисления скорости определенного судна, в
частности "Титаника", нужно передать объекту Titanic сообщение, которое
побудит его обратиться к ассоциированной процедуре в контексте данных о компонентах
скорости именно этого объекта. Titanic — это экземпляр класса ships, от которого
он унаследовал процедуру speed. Все это представляется довольно очевидным, но
описанный механизм срабатывает только в случае, если соблюдаются следующие соглашения.
Во-первых,
программа, разработанная в расчете на этот механизм, должна "учредить"
и следовать в дальнейшем определенному протоколу или "контракту", определяющему
способ взаимодействия между объектами. Другими словами, интерфейс обмена сообщениями
между объектами должен быть досконально продуман, а правила этого интерфейса жестко
соблюдаться. Лучше всего продемонстрировать эту мысль на примере.
Для того чтобы определить компоненту
X текущего положения "Титаника", программа должна послать запрос
объекту Titanic, который имел бы следующий смысл: "передай текущее значение
координаты X". Как в объекте формируется это значение или как оно
хранится — дело только самого объекта и никого более не касается. Ни объекты других
классов, ни какие-либо другие компоненты программы этого не знают. Более того,
внутренний механизм доступа к этой информации должен быть скрыт, чтобы никто не
мог добраться к ней, минуя сам объект. Это соглашение принято называть инкапсуляцией.
Во-вторых,
совершенно очевидна избыточность определения своего метода вычисления скорости
для каждого класса объектов, которые обладают возможностью перемещаться в двумерной
системе координат. Метод, который мы только что определили для класса судов, с
таким же успехом может быть использован и для других движущихся объектов, поскольку
вычисление скорости представляет собой родовую операцию. Поэтому имеет
смысл связать этот метод с каким-нибудь суперклассом транспортных средств, производными
от которого будут классы судов, автомобилей, троллейбусов и т.п. Все эти подклассы
унаследуют родовую операцию у своего базового класса.
Такое наследование — это уже
нечто большее, чем когнитивная экономия или наследование свойств. Выполнение родовых
операций встраивается в механизм обмена сообщениями. Отсылка сообщения— это отнюдь
не вызов определенной процедуры, поскольку вызывающий объект не знает, каким именно
методом отреагирует на это сообщение объект-получатель, от кого он унаследует
этот метод, и будет ли вообще задействован механизм наследования в данном конкретном
случае. Вызывающему объекту известны лишь наименование операции и ее внешние по
отношению к преемнику аргументы. Все остальное — заботы объекта-реципиента сообщения.
7.2.
Формирование объекта класса на языке CLIPS
Ниже
показано, как на языке CLIPS определяется класс ship и формируется экземпляр этого
класса titanic. Сначала определим класс ship, в котором имеются два слота: x-velocity
и y-velocity:
(defclass
ship
(is-a
INITIAL_OBJECT)
(slot
x-velocity (create-accessor read-write))
(slot
y-velocity (create-accessor read-write)) )
Теперь
сформируем экземпляр этого класса,, которому будет дано наименование "Titanic".
Проще всего это сделать с помощью функции definstaces, которая в качестве аргументов
принимает список параметров формируемых экземпляров. Определенные таким способом
экземпляры класса будут инициализироваться при каждом перезапуске интерпретатора
CLIPS.
(definstances ships (titanic of ship
(x-velocity 12) (y-velocity (10)
Завершается
определение созданием обработчика событий для класса ship. Все экземпляры класса
будут использовать этот обработчик для вычисления собственной скорости. Обратите
внимание на то, что член в этом определении ссылается на значение слота того экземпляра
класса, скорость которого требуется вычислить.
(defmessage-handler
ship speed () (sqrt
(
+
{ ?self:x-velocity ?self:x-velocity)
( ?self:y-velocity ?self:y-velocity)))
)
Если файл со всеми представленными выше выражениями загрузить в среду CLIPS, а затем ввести с клавиатуры (send [titanic] speed), то в ответ интерпретатор CLIPS выведет скорость объекта titanic.
| Maya 3D графика в кино и телевидении Воздействие испытаний ядерного оружия на здоровье населения Объектно-ориентированный язык программирования Java Объектно-ориентированное программирование Delphi Библиотека визуальных компонентов VCL и ее базовые классы Кроссплатформенное программирование для Linux Элементы управления Win32 Элементы управления Windows XP Файлы и устройства ввода/вывода Что такое экспертная система? Объектно-ориентированное программирование Инструментальные средства разработки экспертных систем Программирование на языке CLIPS Критерии и количественные характеристики надежности Расчет характеристик надежности невостанавливаемых резервированных изделий Расчет надежности системы с постоянным резервированием Интегрирование тригонометрических функций ; |