Основы объектно-ориентированного программирования

         

Конфликт при совместном использовании: отмена определения и соединение компонентов


Предположим вначале, что две версии наследуются под одним и тем же именем. Это случай совместного использования. Одному имени должен в точности соответствовать один компонент. Возможны три ситуации.

  1. Если одна версия отложена, а другая - эффективна, то сложностей не возникает, будет использован эффективный вариант компонента. Заметим, что этот случай явно предусмотрен правилом одного имени: речь в нем идет лишь о конфликте имен двух эффективных версий.
  2. Каждая версия эффективна, однако обе они переопределяются в D в предложении redefine. Проблемы снова не возникает, поскольку обе версии сливаются в одну, переопределяемую в тексте класса.
  3. Обе версии эффективны, но обе не переопределяются, тогда действительно возникает конфликт имен. Класс D будет отвергнут, как нарушающий правило одного имени.

Нередко (3) означает ошибку: создана неоднозначность имен, и ее необходимо исправить. Тривиальным решением проблемы является переименование одного из вариантов, но тогда мы от рассматриваемого случая совместного использования переходим к репликации, изучаемой ниже.

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

Правила переопределения дают возможность переопределить компонент f как отложенный, хотя для этого и потребуется ввести промежуточный класс, скажем C', - наследника C, единственная роль которого - в переопределении отложенного f . Затем класс D должен быть порожден не от C, а от C'. Сложно и некрасиво. Вместо этого нам нужен простой языковой механизм: undefine. В секции наследования класса он приводит к появлению нового предложения:

class D inherit B C undefine f end feature ... end

Синтаксически предложение undefine следует за rename (всякая отмена определения должна действовать на окончательный вариант имени компонента), но до redefine (прежде, чем что-то переопределять, мы должны позаботиться об отмене ненужных определений).


Признаком того, что предлагаемый языковой механизм желателен, почти всегда является его направленность на решение нескольких проблем (соответственно, плохой механизм создает больше проблем, чем решает). Механизм отмены определений отвечает этому требованию: он позволяет соединять компоненты в условиях множественного (не обязательно - дублируемого) наследования. Пусть мы хотим свести воедино две абстракции:


Рис. 15.22.  Два родителя и слияние компонентов

Мы хотим, чтобы D трактовал f и g как один компонент. Очевидно, это возможно лишь при условии совместимости семантики и сигнатур обоих компонентов (числа и типов аргументов и результата, если он есть). Допустим, что имена компонентов различны, и мы хотели бы сохранить имя f. Добиться желаемого можно, объединив переименование с отменой определения:

class D inherit B C rename g as f undefine f end feature ... end

B получил полное превосходство над C, передавая классу D как сам компонент, так и его имя. Возможны и другие сочетания: компонент можно получить от одного из родителей, имя - от другого; можно переименовать оба компонента, присвоив им новое имя в D.

Еще один, более "симметричный" вариант соединения компонентов, заключается в замене обоих унаследованных вариантов на новый компонент. Достаточно указать оба компонента в предложении redefine, убедившись предварительно, что оба компонента имеют одно и то же финальное имя (добавив, если надо, выражение rename). В результате конфликта имен не возникнет (случай (2)), а объединение двух вариантов даст новый компонент.


Содержание раздела