7.2.2.
Проблема наложения методов
Та простая
картина, которая вырисовывается из представленного выше механизма прямого наследования,
несколько усложняется, если мы попытаемся заменить прямое наследование множественным.
В главе 6 уже отмечалось, что это может привести к неоднозначности в наследовании
свойств. Но в контексте объектно-ориентированного подхода при множественном
наследовании появляется и неоднозначность поведения.
С этой
проблемой впервые столкнулись при разработке объектно-ориентированного языка
FLAVORS, который поддерживает множественное наследование и наложение методов
[Cannon, 1982]. Язык FLAVORS позволяет объектам иметь несколько родителей
и таким образом наследовать процедуры и данные из нескольких источников. Для
FLAVORS характерна не иерархия объектов, а гетерархия. Если графически изобразить
отношения между разными объектами в FLAVORS, то схема будет больше походить
на решетку, чем на дерево. Каков во всем этом смысл? Рассмотрим следующий пример,
взятый из статьи Кэннона.
Отображение
окон на дисплее рабочей станции реализуется, как правило, с использованием объектно-ориентированного
стиля программирования. Будем считать, что окна на экране дисплея представлены
в виде LISP-объектов, в каждом из которых записаны свойства окна (размеры и
положение на поле экрана) и процедуры работы с окном (открытие, закрытие, перерисовка
и т.п.). Существует несколько разновидностей окон и соответственно объектов
окон — с рамкой, без рамки, со строкой заголовка, без заголовка и т.д.
Класс окно
с рамкой .является подклассом (или производным классом) класса окно. Точно так
же подклассом класса окно является и класс окно с заголовком. В иерархической
системе классы окно с рамкой и окно с заголовком представляют собой отдельные
узлы одного и того же уровня иерархии. Они наследуют определенные методы, например
refresh (освежить), от базового класса окно, но имеют и собственные методы выполнения
таких операций, как перерисовка рамки или строки заголовка.
А теперь предположим,
что нам потребовался еще один вид окна — окно с рамкой и строкой заголовка.
Окно такого типа должно быть представлено новым классом окно с рамкой и заголовком.
В иерархической системе новый класс будет наследником класса окно и независимым
"близким родственником" уже существующих классов окно с рамкой и окно
с заголовком на том же уровне иерархии (рис. 7.2). Но даже интуитивно чувствуется,
что такая организация избыточна. Ведь фактически мы стремимся "смешать"
два набора уже существующих качеств и получить в результате новый комбинированный
набор. Кажется, что целесообразнее сделать новый класс "дитятей" двух
родителей, — классов окно с рамкой и окно с заголовком (рис. 7.3).
Рис. 7.2.
Иерархическая система классов окон
Но здесь возникают
вопросы: а как новый класс будет наследовать процедуры, определенные в двух
базовых классах? Устроит ли нас "смешанное" поведение нового класса?
Эту проблему можно разложить на две составляющие:
Рис. 7.3.
Гетерархическая система классов окон
Для решения этой задачи очень подходит механизм включения в основной метод вставок, которые должны выполняться до или после него. В приведенном выше примере с объектами окон можно скомпоновать метод отрисовки окна с рамкой и строкой заголовка таким образом, чтобы новый класс использовал унаследованный от класса окно метод refresh и, кроме того, специализированные методы, унаследованные от каждого из ближайших родителей и выполняемые после основного refresh. При этом должен четко соблюдаться порядок выполнения унаследованных операций и вставок, поскольку его изменение может привести к нежелательному эффекту. В нашем примере после выполнения метода "прародителя" окно нужно выполнить сначала вставку, унаследованную от класса окно с рамкой, а потом вставку, унаследованную от класса окно с заголовком. В противном случае при вычерчивании рамки будет затерта строка заголовка.