Шаг 112.
Объектно-ориентированная система FLAVOR. Наследование

    На этом шаге мы рассмотрим реализацию механизма наследования.

    Рассмотрим два класса A и B. Обозначим P(A) множество переменных экземпляра и методов класса A, а P(B) - множество переменных экземпляра и методов класса B. Множества P(A) и P(B) могут находиться в следующих взаимоотношениях:

  1. P(A) и P(B) не пересекаются, т.е. у объектов классов нет общих переменных экземпляра и методов.
  2. P(A) и P(B) пересекаются, следовательно, у них есть как общие, так и различные переменные экземпляра и методы.
  3. P(A) содержит множество P(B) или наоборот.

    Если P(A) содержит множество P(B), будем говорить, что класс A является подклассом (Subclass) класса B и соответственно класс B является надклассом (Superclass) класса A. Если же переменные экземпляра и методы совпадают, но тогда мы имеем дело с тем же классом.

    Таким образом, у подкласса всегда больше переменных экземпляра и методов, чем у содержащего его надкласса. Его объекты являются в отношении, например, некоторых переменных экземпляра более конкретными (специфичными) по сравнению с объектами надкласса, которые в свою очередь являются более универсальными.

    Теперь ясно, что необходим специальный механизм для формирования иерерхической структуры классов. Этой цели служит механизм наследования (Inheritance Mechanism), содержащийся в той или иной форме во всех объектно-ориентированных системах.

    Посредством механизма наследования новый определяемый класс может автоматически унаследовать переменные экземпляра и методы класса, определенного как его надкласс. С помощью наследования достигается то, что более общие свойства, связанные с типом более высокого уровня, нет необходимости специально определять в связи с типами нижнего уровня. Достаточно указать, из какого надкласса они наследуются.

    Например:

   ; Определение флэйвера с именем ТЕЛО
   (DEFFLAVOR ТЕЛО                           ; Имя флэйвера
      (
        (X 0) (Y 0) (ЦЕНТР-X СОЛНЦЕ-X)       ; Свойства
        (ЦЕНТР-Y СОЛНЦЕ-Y) РАДИУС-ОРБИТЫ     ; флэйвера
        (СЛЕД NIL) (УГОЛ 1) СКОРОСТЬ РАЗМЕР  ;
      )
      ()                                     ; Надклассы
      (:SETTABLE-INSTANCE-VARIABLES ЦЕНТР-X ЦЕНТР-Y)
                                             ; Режимы
   )

    Планеты и спутники определим как подкласс флэйвера ТЕЛО

   (DEFFLAVOR ПЛАНЕТА               ; Имя флэйвера
      ( (СПИСОК-СПУТНИКОВ NIL) )    ; Свойства флэйвера
      (ТЕЛО)                        ; Надклассы
      :SETTABLE-INSTANCE-VARIABLES  ; Режимы
      :GETTABLE-INSTANCE-VARIABLES
   )
   ; -----------------
   (DEFFLAVOR СПУТНИК
      ()                            ; Свойства флэйвера
      (ТЕЛО)                        ; Надклассы
   )

    Таким образом, можно упростить определения объектов и сэкономить усилия на их поддержку. Изменения можно локализовать лишь в одном изменяемом месте программы.

    В разных системах приняты различные соглашения о том, какова может быть топология структур наследования. Например, в языке Smalltalk у класса может быть только один надкласс, но несколько подклассов. В этом случае возможны лишь структуры наследования в виде дерева.

    Система Flavor допускает и циклическое определение, но исключает рекурсивное наследование. Структура определений может быть ориентированным графом, но система распознает циклические определения и обрывает в этом случае наследование. Каждое свойство учитывается лишь один раз.

    Учтите, что даже такие многообразные способы наследования могут оказаться ограниченными. Например, в математике и в естественном языке достаточно часто используются рекурсивные типы. Однако для большинства практических применений достаточно ациклических структур. В этом случае класс наследует свойства не только классов, непосредственно стоящих над ним, но и свойства их надклассов, т.е. свойства всех классов, расположенных выше.

    Если класс наследует свойства из многих надклассов, то встает вопрос о порядке наследования, когда одноименные свойства наследуются из надклассов, находящихся в различных ветвях, или если один и тот же класс встречается в структуре несколько раз. В каком порядке находятся классы и в каком порядке учитываются свойства?

    В принципе возможными последовательностями поиска являются, например, поиск в глубину (Depth First) и по уровням (Level First). В каждой ветви возможными порядками прохождения являются прямой и обратный. Под прямым порядком понимают порядок, в котором узел дерева (или графа) проходится раньше прохождения в том же порядке его поддеревьев. Обратный порядок можно определить аналогично.

    В системе Flavor в прохождении иерархии флэйверов применяется поиск в глубину и прямой порядок обхода. Свойства наследуются в том порядке, в котором надклассы указаны в описании класса. Класс с тем же именем повторно не проходится. Предположим, что иерархия классов выглядит так [1, с.114-115]:


Рис.1. Иерархия классов

    В этом случае порядок прохода получился бы следующий:

   М1 -->М2 -->М4 -->М5 -->М3
Обратите внимание, что класс М4 проходится только один раз.

    Поскольку в разных классах могут быть одноименные, но различные по определениям методы, то для избежания конфликтов нужно договориться о том, какой из них останется в силе. Здесь разумно ограничиться двумя возможностями: в силе остается либо первый метод, либо последний.

    В системе Flavor в силе остается первое встретившееся свойство. Если, например, в классе М5 был бы некоторый метод с тем же именем, что и в классе М4, то метод М5 не учитывался бы. Система, однако, дает возможность изменить заложенный по умолчанию порядок следования. Если, например, определяется, что в силе остается последний метод, то говорят, что он перекрывает предыдущие вхождения.

    Переменные экземпляра подкласса определяются как множество своих переменных экземпляра и переменных экземпляра надклассов. Если у надкласса некоторого класса есть переменная экземпляра с тем же именем, то она является общей для класса (Shared Instance Variable). Общие переменные можно использовать для связи между классами. Одну и ту же переменную подкласса можно, например, присваивать и читать методами, определенными в разных надклассах.

    Кроме определенных пользователем объектов и классов объектная система обычно содержит общие системные классы, свойства которых доступны всем. Такие классы мы назовем базовыми классами.

    Например, в системе Flavor есть базовый ванилиновый флэйвер (Vanilla Flavor). Его свойства и методы наследуются автоматически всеми объектами.

    Базовый класс может содержать различные системные свойства и действия, такие как:


    Пример. Программа "Солнечная система" [1, п.4.5] для muLISP87. Она рассчитана на использование системы объектно-ориентированного программирования Flavor, содержащейся в файле FLAVORS.LSP (автор: Peter Ohler), поставляемом вместе с системой muLISP87.
   ; Астрономическая модель, представляющая и делающая более
   ; наглядным строение и функционирование "кусочка" Солнеч-
   ; ной системы. Модель описывает Солнце и планеты Меркурий,
   ; Венеру, Землю, Марс и их спутники.
   (SETQ СОЛНЦЕ-X 150 СОЛНЦЕ-Y 100)
   (SETQ ДИАМЕТР-СОЛНЦА 10 ОРБИТА-ЗЕМЛИ 149)
   (SETQ КРАСНЫЙ (+ 2 128))
                         ;Красный цвет с использованием XOR
   (SETQ СВЕТЛЫЙ (+ 1 128))
                         ;Циановый цвет с использованием XOR
   ; -------------------------------------------------------
   (DEFFLAVOR ТЕЛО
   ; Определение базового класса с именем ТЕЛО
      (
         (X 0)
         (Y 0)
         (ЦЕНТР-X СОЛНЦЕ-X)
         (ЦЕНТР-Y СОЛНЦЕ-Y)
         РАДИУС-ОРБИТЫ
         (СЛЕД NIL)
         (УГОЛ 1)
         СКОРОСТЬ
         РАЗМЕР
      )
      ()
      (:SETTABLE-INSTANCE-VARIABLES ЦЕНТР-X ЦЕНТР-Y)
   )
   ; -----------------------------------------
   (DEFFLAVOR ПЛАНЕТА ((СПИСОК-СПУТНИКОВ NIL))
   ; Планеты определим как подкласс класса ТЕЛО
      (ТЕЛО)
      :SETTABLE-INSTANCE-VARIABLES
      :GETTABLE-INSTANCE-VARIABLES
   )
   ; -------------------
   (DEFFLAVOR СПУТНИК ()
   ; Спутники определим как подкласс класса ТЕЛО
      (ТЕЛО)
   )
   ; -------------------------
   (DEFMETHOD (ТЕЛО :ВРАЩАЙСЯ)
   ; Определение метода :ВРАЩАЙСЯ класса ТЕЛО
      (ОТНОСИТ-УГОЛ ПЕРЕМЕЩЕНИЕ OldX OldY)
      (SETQ ОТНОСИТ-УГОЛ СКОРОСТЬ ПЕРЕМЕЩЕНИЕ)
      (LOOP
         ( (= ОТНОСИТ-УГОЛ 0) )
         (IF  (> ОТНОСИТ-УГОЛ 10)
            (SETQ ОТНОСИТ-УГОЛ (- ОТНОСИТ-УГОЛ 10)
                  ПЕРЕМЕЩЕНИЕ 10)
            (SETQ ПЕРЕМЕЩЕНИЕ ОТНОСИТ-УГОЛ ОТНОСИТ-УГОЛ 0)
         )
         (SETQ УГОЛ (+ УГОЛ ПЕРЕМЕЩЕНИЕ))
         (SETQ OLDX X OLDY Y)
         (SETQ X (TRUNCATE (+ ЦЕНТР-X (* (SIN-DEG УГОЛ)
                                         РАДИУС-ОРБИТЫ))))
         (SETQ Y (TRUNCATE (+ ЦЕНТР-Y (* (COS-DEG УГОЛ)
                                         РАДИУС-ОРБИТЫ))))
         (IF  (OR (/= X OLDX) (/= Y OLDY))
            (PROG1
               (PLOT-CIRCLE OLDX OLDY РАЗМЕР КРАСНЫЙ)
                                        ; Стереть окружность
               (PLOT-CIRCLE X Y РАЗМЕР КРАСНЫЙ)
                                        ; Красная окружность
               (IF СЛЕД (PLOT-DOT X Y СВЕТЛЫЙ) NIL)
            )
         )
      )
   )
   ; -------------------------------------------------------
   (DEFMETHOD (ПЛАНЕТА :AFTER :ВРАЩАЙСЯ) (ОЧЕРЕДНОЙ-СПУТНИК)
      (SETQ S СПИСОК-СПУТНИКОВ)
      (LOOP
         ( (NULL S) )
         (SETQ ОЧЕРЕДНОЙ-СПУТНИК (POP S))
         (SEND ОЧЕРЕДНОЙ-СПУТНИК :SET-ЦЕНТР-X X)
         (SEND ОЧЕРЕДНОЙ-СПУТНИК :SET-ЦЕНТР-Y Y)
         (SEND ОЧЕРЕДНОЙ-СПУТНИК :ВРАЩАЙСЯ)
      )
   )
   ; -------------------------------------------
   ; Тригонометрические функции системы muLISP87
   ; -------------------------------------------
   (DEFUN SIN-DEG (ANGLE)
   ; Возвращает значение синуса аргумента,
   ; выраженного в градусах
      ( (MINUSP ANGLE)
           (SETQ ANGLE (DIVIDE (REM (- ANGLE) 360) 45))
      (- (SIN-COS-DEG (CAR ANGLE) (CDR ANGLE))) )
      (SETQ ANGLE (DIVIDE (REM ANGLE 360) 45))
      (SIN-COS-DEG (CAR ANGLE) (CDR ANGLE))
   )
   ; --------------------
   (DEFUN COS-DEG (ANGLE)
   ; Возвращает значение косинуса аргумента,
   ; выраженного в градусах
      (SETQ ANGLE (DIVIDE (REM (ABS ANGLE) 360) 45))
      (SIN-COS-DEG (+ 2 (CAR ANGLE)) (CDR ANGLE))
   )
   ; -------------------------------
   (DEFUN SIN-COS-DEG (N45DEG RESID)
      ((> N45DEG 3)
      (- (SIN-COS-DEG (- N45DEG 4) RESID)) )
      ( (ZEROP N45DEG) (REDUCED-SIN RESID) )
      ( (EQ N45DEG 1) ( (ZEROP RESID) 0.70710678 )
      (REDUCED-COS (- 45 RESID)))
      ( (EQ N45DEG 2) (REDUCED-COS RESID) )
      ( (ZEROP RESID) 0.70710678 )
      (REDUCED-SIN (- 45 RESID))
   )
   ; ----------------------
   (DEFUN REDUCED-SIN (DEG)
      (/ (* DEG (+ 1324959969 (* (SETQ DEG (* DEG DEG))
                                 (+ -67245 DEG))))
     75914915920)
   )
   ; ----------------------
   (DEFUN REDUCED-COS (DEG)
      (SETQ DEG (* DEG DEG))
      (/ (+ 266153374 (* DEG (+ -40518 DEG)))
         266153374) )
   ; -------------------------
   (DEFUN SOLAR ()
   ; Основная функция
      (SETQ МАРС
            (MAKE-INSTANCE ПЛАНЕТА
                           :РАДИУС-ОРБИТЫ 114
                           :СКОРОСТЬ 0.053
                           :РАЗМЕР 4)
      )
      (SETQ ФОБОС
            (MAKE-INSTANCE СПУТНИК
                           :ЦЕНТР-X 48
                           :ЦЕНТР-Y 48
                           :РАДИУС-ОРБИТЫ 7
                           :СКОРОСТЬ 114.4
                           :РАЗМЕР 3)
      )
      (SETQ МЕРКУРИЙ
            (MAKE-INSTANCE ПЛАНЕТА
                           :РАДИУС-ОРБИТЫ 29
                           :СКОРОСТЬ 0.416
                           :РАЗМЕР 3)
      )
      (SETQ ВЕНЕРА
            (MAKE-INSTANCE ПЛАНЕТА
                           :РАДИУС-ОРБИТЫ 54
                           :СКОРОСТЬ 0.416
                           :РАЗМЕР 5)
      )
      (SETQ ЗЕМЛЯ
            (MAKE-INSTANCE ПЛАНЕТА
                           :РАДИУС-ОРБИТЫ 84
                           :СКОРОСТЬ 0.1
                           :РАЗМЕР 6)
      )
      (SETQ ЛУНА
            (MAKE-INSTANCE СПУТНИК
                           :ЦЕНТР-X 48
                           :ЦЕНТР-Y 48
                           :РАДИУС-ОРБИТЫ 15
                           :СЛЕД T
                           :СКОРОСТЬ 1.3
                           :РАЗМЕР 3)
      )
      (SETQ ДЕЙМОС
            (MAKE-INSTANCE СПУТНИК
                           :ЦЕНТР-X 48       ;(SEND МАРС :X)
                           :ЦЕНТР-Y 48       ;(SEND МАРС :Y)
                           :РАДИУС-ОРБИТЫ 12
                           :СКОРОСТЬ 30.4
                           :РАЗМЕР 3)
      )
      ; "Присваивание" спутников Земле и Марсу
      (SEND ЗЕМЛЯ :SET-СПИСОК-СПУТНИКОВ (LIST ЛУНА))
      (SEND МАРС  :SET-СПИСОК-СПУТНИКОВ (LIST ФОБОС ДЕЙМОС))
      ; Запуск Солнечной системы
      (VIDEO-MODE 4)
      (PLOT-CIRCLE СОЛНЦЕ-X СОЛНЦЕ-Y
                   (- ДИАМЕТР-СОЛНЦА 3) КРАСНЫЙ)
      (LOOP
         (PLOT-CIRCLE СОЛНЦЕ-X СОЛНЦЕ-Y
                      (- ДИАМЕТР-СОЛНЦА 3) КРАСНЫЙ)
         (PLOT-CIRCLE СОЛНЦЕ-X СОЛНЦЕ-Y
                      (- ДИАМЕТР-СОЛНЦА 1) КРАСНЫЙ)
         (PLOT-CIRCLE СОЛНЦЕ-X СОЛНЦЕ-Y
                      ДИАМЕТР-СОЛНЦА КРАСНЫЙ)
         ; Пусть вращаются планеты!
         (SEND МЕРКУРИЙ :ВРАЩАЙСЯ)
         (SEND ВЕНЕРА   :ВРАЩАЙСЯ)
         (SEND ЗЕМЛЯ    :ВРАЩАЙСЯ)
         (SEND МАРС     :ВРАЩАЙСЯ)
      )
   )
   (SOLAR)
Текст этой библиотеки можно взять здесь.


    Примечание. Приведем алгоритм выполнения данной библиотеки
  1. Загрузить среду программирования muLISP87 (архив можно взять здесь) (файл l.com) и систему Flavor (файл flavors.lsp), выполнив в командной строке следующую команду:
       l.com flavors.lsp
    
  2. На вопрос о запуске демонстрации ответить Нет (нажать клавишу N).
  3. Загрузить содержимое файла l112_1.lsp, где находится приведенная выше программа:
       (LOAD L112_1.LSP)
    
    После завершения загрузки программа запустится на выполнение автоматически.
  4. Для выхода из программы нажать клавишу ESC и далее действовать по подсказкам системы.

    Кроме того, можно выделить абстрактные совокупности свойств в характеристические классы и использовать их в определениях. Характеристический класс может содержать свойства, противоположные или аналогичные свойствам базовых классов в их естественной классификации.

    Например, классы "солнце" и "яблоко" могли бы унаследовать из некоторого класса геометрических свойств свойство "круглый".

    Наиболее важное преимущество объектного программирования, пожалуй, и состоит в том, что оно предлагает механизм абстракции для разбиения задачи и ее решения на части. С другой стороны, оно предполагает, что объекты проблемной области, а также их свойства и зависимости должны быть тщательно проанализированы.

    Чтобы различить классы, можно использовать естественное разбиение объектов на разные классы. Например, живые существа разделяются на млекопитающих, птиц, пресмыкающихся, рыб, насекомых и т.д. Далее они подразделяются на подклассы в соответствии с дополнительными свойствами каждого вида.

   


(1) Хювенен Э., Сеппянен Й. Мир Лиспа. В 2-х т. Т.2: Методы и системы программирования. - М.: Мир, 1990. - 319 с.

    Со следующего шага мы приведем задания для самостоятельного решения.




Предыдущий шаг Содержание Следующий шаг