На этом шаге мы рассмотрим реализацию обмена сообщениями.
Объекты индивидуальны, и у каждого из них есть свое текущее состояние и свой набор соответствующих типу методов. Вызов методов обобщен и его единообразность обеспечивается специальным механизмом, который называется обменом сообщениями. Таким образом, метод объекта можно активизировать, послав соответствующее этому методу сообщение (Message).
Посылка сообщения объекту осуществляется вызовом универсальной функции SEND, в которой объекту передается имя вызываемого метода и используемые им аргументы. Принимающий сообщение объект содержит механизм, который опознает сообщение, выбирает соответствующий ему метод, активизирует его и передает ему содержащиеся в сообщении параметры. Метод вычисляется так же, как обыкновенная функция, и в качестве ответа на сообщение возвращается его значение.
Итак, сообщение посылается формой SEND:
(SEND OBJ METH . VARS)
OBJ - это объект, которому посылается сообщение METH с аргументами VARS. Получающий сообщение объект активизирует соответствующий ему метод и, вообще говоря, возвращает сообщение тому объекту, который послал сообщение.
Например:
; Пусть вращаются планеты! (SEND МЕРКУРИЙ :ВРАЩАЙСЯ) (SEND ВЕНЕРА :ВРАЩАЙСЯ) (SEND ЗЕМЛЯ :ВРАЩАЙСЯ) (SEND МАРС :ВРАЩАЙСЯ) ; Присваивание спутников Земле и Марсу (SEND ЗЕМЛЯ :SET-СПИСОК-СПУТНИКОВ (LIST ЛУНА)) (SEND МАРС :SET-СПИСОК-СПУТНИКОВ (LIST ФОБОС ДЕЙМОС))
Если у объекта нет метода, соответствующего принятому сообщению, то сообщение теряется. Обычно причиной этого является ошибка или по крайней мере исключительная ситуация, к обработке которой нужно быть готовым.
Важно отметить, что вычисление метода может в результате побочного эффекта изменить состояние объекта и быть причиной посылки новых сообщений другим объектам.
После обработки сообщения и после того, как объект перестанет быть активным, он и его состояние сохраняются и объект будет ожидать новых сообщений. Переменные экземпляра и их значения не исчезают, как в случае обыкновенных функций, а их значения сохраняются и могут использоваться, когда объект получит новое сообщение.
Объекты, методы и обмен сообщениями позволяют использовать новый принцип построения программы и вычислений в ней, более общий и гибкий, чем традиционная идея подпрограммы. Ход событий не управляется извне, а осуществляется в зависимости от поведения объектов (их реакции на сообщения).
Объекты являются более независимыми единицами, чем традиционные подпрограммы. Они могут содержать сведения о собственном строении и функционировании, участвовать в управлении вычислением и взаимодействовать с другими объектами, посылая и принимая сообщения.
(DEFFLAVOR SHIP ; Имя флэйвера (NAME X Y X-SPEED Y-SPEED) ; Переменные экземпляра () ; Надклассы отсутствуют :SETTABLE-INSTANCE-VARIABLES ; Режимы :GETTABLE-INSTANCE-VARIABLES ) ; -------- (DEFMETHOD (SHIP :SPEED) () (+ (* X-SPEED X-SPEED) (* Y-SPEED Y-SPEED)) ) % ------------ (DEFUN MAIN () (SETQ SHIP1 (MAKE-INSTANCE 'SHIP)) ; Объекту при создании дадим имя (SETQ SHIP78 (MAKE-INSTANCE 'SHIP :NAME 'Titanik)) ; Объекту дается новое имя (PRINT (SEND SHIP78 :SET-NAME 'BORE)) ; У объекта спрашивается имя (PRINT (SEND SHIP78 :NAME)) ; Объекту присваивается X-SPEED (PRINT (SEND SHIP78 :SET-X-SPEED 3.0)) ; Объекту присваивается Y-SPEED (PRINT (SEND SHIP78 :SET-Y-SPEED 4.0)) ; У объекта спрашивается SPEED (PRINT (SEND SHIP78 :SPEED)) ) ; ---- (MAIN)
(SETQ *ALL-WINDOWS*) (DEFFLAVOR WINDOW ; Базовый флэйвер для окна ( TOP ; Вершина окна в рядах LEFT ; Левый край окна в колонках HEIGHT ; Высота в колонках WIDTH ; Ширина в рядах (CURRENT-ROW 0) ; Позиция курсора (ряд) (CURRENT-COL 0) ; Позиция курсора (колонка) (EXPOSEDP NIL) ; Включено ли (видимо ли?) окно? (BORDER 1) ; Ширина бордюра вокруг окна (BORDER-COLOR 7) ; Цвет бордюра (FOREGROUND 15) ; Цвет переднего плана окна (BACKGROUND 0) ; Цвет фона окна ) () ; Надклассы отсутствуют :SETTABLE-INSTANCE-VARIABLES :GETTABLE-INSTANCE-VARIABLES ) ; --------------------------------- (DEFMETHOD (WINDOW :AFTER :INIT) () ; После создания окна, метод добавляет его имя к ; списку окон (PUSH SELF *ALL-WINDOWS*) ) ; ---------------------------- (DEFMETHOD (WINDOW :EXPOSE) () ; Метод позволяет высветить окно: он формирует окно с за- ; данными границами, устанавливает курсор в нужную пози- ; цию, если окно уже высвечено; в противном случае очища- ; ет окно ( (SEND SELF :EXPOSEDP) (MAKE-WINDOW (+ TOP BORDER) (+ LEFT BORDER) (- HEIGHT (* 2 BORDER)) (- WIDTH (* 2 BORDER)) ) (SET-CURSOR CURRENT-ROW CURRENT-COL) ) (SEND SELF :REFRESH) ) ; ----------------------------- (DEFMETHOD (WINDOW :REFRESH) () ; Метод "очищает" окно требуемым цветом и устанавливает ; выведенные на экран переменные в TRUE (BACKGROUND-COLOR BORDER-COLOR) (FOREGROUND-COLOR 0) (MAKE-WINDOW TOP LEFT HEIGHT WIDTH) (CLEAR-SCREEN) (BACKGROUND-COLOR BACKGROUND) (FOREGROUND-COLOR FOREGROUND) (MAKE-WINDOW (+ TOP BORDER) (+ LEFT BORDER) (- HEIGHT (* 2 BORDER)) (- WIDTH (* 2 BORDER)) ) (CLEAR-SCREEN) (SETQ CURRENT-ROW 0 CURRENT-COL 0) (SETQ EXPOSEDP 'T) ) ; ------------------------------------------- (DEFMETHOD (WINDOW :AFTER :REFRESH) (WINDOWS) ; После того, как окно очищено, метод проверяет все ос- ; тальные окна и если они перекрываются новым окном, то ; высвечивает их вновь (SETQ WINDOWS (REMOVE SELF *ALL-WINDOWS*)) (LOOP ( (NULL WINDOWS) ) ( ( (NO-OVERLAP TOP LEFT HEIGHT WIDTH (CAR WINDOWS)) (POP WINDOWS) ) (SEND (CAR WINDOWS) :SET-EXPOSEDP NIL) (POP WINDOWS) ) ) ) ; ------------------------------------------------- (DEFMETHOD (WINDOW :BEFORE :PRINT) (STR ROW COLUMN) ; Прежде, чем Вы что-либо выводите в окно, оно должно ; появиться (SEND SELF :EXPOSE) ) ; ------------------------------------------------- (DEFMETHOD (WINDOW :BEFORE :PRINC) (STR ROW COLUMN) ; Прежде, чем Вы что-либо выводите в окно, оно должно ; появиться (SEND SELF :EXPOSE) ) ; ----------------------------------------- (DEFMETHOD (WINDOW :PRINT) (STR ROW COLUMN) ; Печатает STR в заданных ROW и COLUMN окна (IF (AND (INTEGERP ROW) (INTEGERP COLUMN)) (SET-CURSOR ROW COLUMN)) (PRINT STR) (SETQ CURRENT-ROW (ROW) CURRENT-COL (COLUMN)) ) ; ----------------------------------------- (DEFMETHOD (WINDOW :PRINC) (STR ROW COLUMN) ; Печатает STR в заданных ROW и COLUMN окна (IF (AND (INTEGERP ROW) (INTEGERP COLUMN)) (SET-CURSOR ROW COLUMN)) (PRINC STR) (SETQ CURRENT-ROW (ROW) CURRENT-COL (COLUMN)) ) ; ------------------------- (DEFWHOPPER (WINDOW :PRINT) (STR ROW COLUMN W1 W2 W3 W4 R C) ; Мы хотим вернуться в текущее окно после работы в ; другом окне, поэтому мы находим размеры текущего ; окна, а затем переустанавливаем их после выполнения ; метода :PRINT (SETQ R (MAKE-WINDOW) W1 (POP R) W2 (POP R) W3 (POP R) W4 (POP R)) (SETQ R (ROW) C (COLUMN)) (IF (NO-OVERLAP W1 W2 W3 W4 SELF) (CONTINUE-WHOPPER STR ROW COLUMN)) (MAKE-WINDOW W1 W2 W3 W4) (SET-CURSOR R C) ) ; ------------------------- (DEFWHOPPER (WINDOW :PRINC) (STR ROW COLUMN W1 W2 W3 W4 R C) ; Мы хотим вернуться в текущее окно после работы в ; другом окне, поэтому мы находим размеры текущего ; окна, а затем переустанавливаем их после выполнения ; метода :PRINC (SETQ R (MAKE-WINDOW) W1 (POP R) W2 (POP R) W3 (POP R) W4 (POP R)) (SETQ R (ROW) C (COLUMN)) (IF (NO-OVERLAP W1 W2 W3 W4 SELF) (CONTINUE-WHOPPER STR ROW COLUMN)) (MAKE-WINDOW W1 W2 W3 W4) (SET-CURSOR R C) ) ; --------------------------------------------------- (DEFUN NO-OVERLAP (TOP1 LEFT1 HEIGHT1 WIDTH1 WINDOW2) ; Перекрывает ли окно прямоугольник со сторонами, ; образуемыми TOP1 LEFT1 HEIGHT1 WIDTH1 ( (LAMBDA (TOP2 LEFT2 HEIGHT2 WIDTH2 BOTTOM1 BOTTOM2 RIGHT1 RIGHT2) (SETQ BOTTOM1 (+ TOP1 HEIGHT1) BOTTOM2 (+ TOP2 HEIGHT2) RIGHT1 (+ LEFT1 WIDTH1) RIGHT2 (+ LEFT2 WIDTH2)) ( (OR (<= TOP1 TOP2 BOTTOM1) (<= TOP1 BOTTOM2 BOTTOM1) (<= TOP2 BOTTOM1 BOTTOM2)) ( (OR (<= LEFT1 LEFT2 RIGHT1) (<= LEFT1 RIGHT2 RIGHT1) (<= LEFT2 RIGHT1 RIGHT2)) NIL)) 'T ) (SEND WINDOW2 :TOP) (SEND WINDOW2 :LEFT) (SEND WINDOW2 :HEIGHT) (SEND WINDOW2 :WIDTH) ) ) ;---------------------------------------- ; Изготовление экземпляра флэйвера WINDOW (SETQ SMALL (MAKE-INSTANCE 'WINDOW :TOP 2 :LEFT 10 :HEIGHT 8 :WIDTH 60)) ; --------------------------------------- ; Изготовление экземпляра флэйвера WINDOW (SETQ TINY (MAKE-INSTANCE 'WINDOW :TOP 3 :LEFT 30 :HEIGHT 8 :WIDTH 20 :BORDER 2)) ; --------------------------------------- ; Изготовление экземпляра флэйвера WINDOW (SETQ MAIN (MAKE-INSTANCE 'WINDOW :TOP 12 :LEFT 0 :HEIGHT 12 :WIDTH 80)) ; ------------------------------------------ (SEND MAIN :EXPOSE) ; Включить окно MAIN (WRITE-LINE "Послано сообщение (SEND SMALL :PRINC 'HELLO 2 2)") (SEND SMALL :PRINC 'HELLO 2 2) (WRITE-LINE "Послано сообщение (SEND TINY :PRINC 'HELLO 2 2) ") (SEND TINY :PRINC 'HELLO 2 2) (SEND MAIN :REFRESH) ; Очистили окно MAIN (SEND SMALL :REFRESH) ; Очистили окно SMALL (SEND TINY :REFRESH) ; Очистили окно TINY
(FUNCALL (GET ОБЪЕКТ МЕТОД) АРГУМЕНТЫ)
Так становится явной связь между объектно-ориентированным мышлением и списком свойств. Список свойств символа - это в некотором смысле тоже объект. Его методами являются, например, функции GET, PUT, REMPROP.
На следующем шаге мы поговорим о неследовании.