Руководство разработчика

Полиморфизм

Полиморфизм в объектно-ориентированном программировании играет крайне важную роль. Иерархии типов являются мощным средством выражения общности и различия понятий. Методы родительских типов в иерархии могут перекрываться в дочерних типах. Свойство полиморфизма проявляется в том, что в результате выполнения одной инструкции вызова могут вызываться различные методы в зависимости от типа объекта. На этапе компиляции не известно вызов какого метода будет осуществлен. Поэтому в случае вызова перекрытых методов "ключом" является тип объекта. Применение полиморфизма объектов полезно при наличии некоторого схожего действия (например, вычисления какой-то величины) и различной его реализации для разных объектов (например, использование различных способов вычисления). Таким образом, полиморфизм позволяет в точности не знать, какого именно типа объект подвергается обработке. Достоинства такого подхода очевидны:

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

Проиллюстрируем сказанное примером. Пусть требуется составить набор геометрических фигур на плоскости и уметь вычислять площадь любой фигуры в наборе. Определим общий базовый тип Фигура, который содержит название фигуры (поле строкового типа название) и метод расчета ее площади (ПЛОЩАДЬ). Этим типом выражается общность понятий поставленной задачи. Теперь определим различия для нескольких конкретных фигур. Названия фигур будем задавать, перекрывая значение по умолчанию поля название. Самой простой конкретной фигурой является точка, на плоскости она задается двумя координатами, полями числового типа x и y. Метод ПЛОЩАДЬ, определенный для типа Фигура, в отношении типа Точка нас устраивает и не требует перекрытия. Определим типы Окружность и Прямоугольник, родителем которых является Точка: центр окружности и левый верхний угол прямоугольника соответственно. Для этих фигур добавлены необходимые поля: "радиус" для окружности, "высота" и "ширина" для прямоугольника, а также перекрыты методы расчета площади по школьным формулам.

Пример:

ТИП Фигура 
[
  СТРОКА: название;
  ФУНКЦИЯ ЧИСЛО: ПЛОЩАДЬ 
    РЕЗУЛЬТАТ = 0 
  КОНЕЦ_ФУНКЦИИ
]

ТИП Точка(Фигура)
[
  ЧИСЛО: х, у;
  СТРОКА: название = "Точка";
]

КОНСТ Пи = 3.14159;

ТИП Окружность(Точка)
[
  ЧИСЛО: рад = 10; 
  СТРОКА: название = "Окружность";

  ФУНКЦИЯ ЧИСЛО: ПЛОЩАДЬ 
    РЕЗУЛЬТАТ = Пи * рад * рад 
  КОНЕЦ_ФУНКЦИИ
]

ТИП Прямоугольник(Точка)
[
  ЧИСЛО: шир = 10;
  ЧИСЛО: выс = 10;
  СТРОКА: название = "Прямоугольник";

  ФУНКЦИЯ ЧИСЛО: ПЛОЩАДЬ 
    РЕЗУЛЬТАТ = шир * выс
  КОНЕЦ_ФУНКЦИИ
]

ВЫЧИСЛИТЬ
  ПЕРЕМ *(Фигура): ф[ЧИСЛО];

  ф[1] = Прямоугольник[шир = 5]
  ф[2] = Окружность[х = 3, у = 3, рад = 3]
  ф[3] = Прямоугольник[]
  ф[4] = Окружность[]
  ф[5] = Точка[х = 5, у = 2]

  ЦИКЛ ДЛЯ (и = 1, 5)
    СООБЩЕНИЕ(Фигура(ф[и]).название + " S = " + СТР(Фигура(ф[и]).ПЛОЩАДЬ))
  КОНЕЦ_ЦИКЛА
КОНЕЦ

Далее составим набор из пяти различных фигур, воспользовавшись контейнерной переменной "ф" со смешанным типом элементов. И наконец, обработаем фигуры однотипным образом (в примере - выведем на экран). Для возможности вызова метода ПЛОЩАДЬ и обращения к полю "название" необходимо уточнить тип элементов "ф" до типа Фигура. Благодаря полиморфному вызову перекрытых методов для каждого элемента контейнерной переменной будет вызван нужный метод расчета площади. Например, для четвертой фигуры будет выведено: "Окружность S = 314.159". В приведенном примере все фигуры набора либо являются точкой, либо потомками типа Точка. Поэтому допустимо уточнение не до типа Фигура, а до типа Точка. Это позволит получить доступ к полям "x" и "y" всех используемых фигур, но уменьшит общность алгоритма обработки. Так, если определить тип Многоугольник, производный от типа Фигура,

ТИП Многоугольник(Фигура)
[
  Точка: точки[ЧИСЛО];
  СТРОКА: название = "Многоугольник";

  ФУНКЦИЯ ЧИСЛО: ПЛОЩАДЬ 
     ..
  КОНЕЦ_ФУНКЦИИ
]

,где координаты точек задаются контейнерным полем "точки", то уточнение этого типа до типа Точка приведет к ошибке времени выполнения. Однако многоугольник является производным от типа Фигура, имеет название и метод расчета площади, а значит, может быть помещен в набор и правильно обработан в нашем примере. Как и где хранятся координаты точек, как рассчитывается площадь - это детали реализации объектного типа. Свойство полиморфизма позволяет использовать объекты, не задумываясь о деталях их реализации. В самом деле, цикл вывода названия и площади фигуры на экран был написан нами до того, как речь зашла о многоугольнике. Введение многоугольника в пример не потребовало изменения цикла вывода, достаточно лишь реализовать сам тип многоугольника. Данный пример можно легко переписать без использования явного уточнения типа. Для этого надо определить функцию, принимающую параметр-переменную типа Фигура:

ФУНКЦИЯ Вывод(ПЕРЕМ Фигура: фигура)
  СООБЩЕНИЕ(фигура.название + " S = " + СТР(фигура.ПЛОЩАДЬ))
КОНЕЦ_ФУНКЦИИ

Вызов функции произведем в том же цикле, но уточнения типа при передаче параметра не требуется:

ЦИКЛ ДЛЯ (и = 1, 5)
  Вывод(ф[и])
КОНЕЦ_ЦИКЛА

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


См. также: