Сложный полиморфизм или создание полиморфных объектов.

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

при передаче объекта типа класса-потомка в качестве фактического параметра подпрограмме, в которой этот параметр описан, как параметр типа класса-родителя (явно - в списке параметров или неявно – в качестве внутреннего параметра, используемого при вызове методов - self или this);

2) при работе с указателями, когда указателю на объект класса-родителя присваивается адрес объекта класса-потомка.

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

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

С помощью механизма позднего связывания реализуется оперативная перестройка программы в соответствии с типами используемых объектов.

Пример сложного полиморфизма

Пусть родительский класс содержит два метода Out и Print, один из которых вызывает другой. Класс-потомок наследует метод Out, но имеет собственный метод Print. Метод Out наследуется и может быть вызван как для объекта класса-родителя, так и для объекта класса-потомка (рис. 11)

.

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

При вызове метода Out для объекта класса-потомка необходимо обеспечить, чтобы этот метод вызывал метод Print потомка, а не родителя (рис. 12). Определить, для объекта какого класса: родителя или потомка вызывается метод Out, можно только на этапе выполнения. Следовательно, для метода Print необходимо обеспечить позднее связывание.

Методы, для которых должно реализовываться позднее связывание называются виртуальными. Для их описания в рассматриваемых далее языках программирования используется служебное слово virtual.

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

Реализация механизма позднего связывания осуществляется с использованием специальной таблицы, получившей название таблицы виртуальных методов (ТВМ). ТВМ создается для каждого класса, имеющего собственные или наследующего виртуальные методы. Она содержит адреса виртуальных методов (рис. 13). Объекты таких классов содержат адрес ТВМ своего класса. При вызове виртуального метода для объекта происходит обращение к ТВМ класса, по которой и определяется требуемый метод.

Рис. 13. Реализация механизма позднего связывания

При использовании полиморфных объектов возникают проблемы с доступом к полям объекта, описанным в классе-потомке: указатель на объект класса-родителя связан с описанием полей класса-родителя, и поля, описанные в классе-потомке, для него «невидимы» (рис. 14). В таких случаях приходится средствами используемого языка явно переопределять тип объекта.

Рис. 14. Необходимость явного указания типа объекта, адресуемого указателем родительского класса

Композиция.

В результате объектной декомпозиции второго и более уровней могут быть получены объекты, находящиеся между собой в отношении включения (рис. 2). Классы для реализации таких объектов могут строиться двумя способами: с использованием наследования или композиции.

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

В тех случаях, когда сходное поведения не просматривается или наследование по каким-либо причинам нецелесообразно, можно использовать композицию классов.

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

Пример Композиции (класс Сообщение )

Визуально сообщение обычно выглядит, как окно с текстом и кнопкой подтверждения (рис. 15).

Рис. 15. Вид окна сообщения

При разработке класса Сообщения попытаемся использовать уже описанный класс Окно_с_текстом. Окно сообщения без кнопки представляет собой объект класса Окно_с_текстом. Кнопка также представляет собой Окно_с_текстом. Попытка использования при разработке множественного наследования приведет к дублированию полей. Чтобы этого избежать, используем для хранения параметров изображения кнопки поле типа Окно_с_текстом.

Класс Сообщение–родитель: класс Окно_с_текстом: