Конфликт имен


Переименование компонентов

Оценка

 

Приведенные примеры наглядно проиллюстрировали мощь и силу механизма множественного наследования. Необходимость его применения подтверждена опытом построения универсальных библиотек [M 1994a].

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

 

 

 

Иногда при множественном наследовании возникает проблема конфликта имен (name clash). Ее решение - переименование компонентов (feature renaming) - не только снимает саму проблему, но и способствует лучшему пониманию природы классов.

 

 

Каждый класс обладает доступом ко всем компонентам своих родителей. Он может использовать их, не указывая тот класс, в котором они были описаны. После обработки inherit в классе class C inherit A ... метод f класса C становится известен как f . То же справедливо и для клиентов: при объявлении сущности x типа C вызов компонента записывается как x.f без каких-либо ссылок на A . Все метафоры "хромают", иначе можно было бы говорить, что наследование - форма усыновления: C усыновляет все компоненты A .

Усыновление не меняет присвоенных имен, и набор имен компонентов данного класса содержит наборы имен компонентов каждого его родителя.

А если родители класса разные компоненты назвали одним именем? Возникает противоречие, поскольку согласно установленному ранее правилу запрещена перегрузка имен: в классе имя компонента обозначает только один компонент. Это правило не должно нарушаться при наличии родителей класса. Рассмотрим пример:

 

class SANTA_BARBARA inherit

LONDON

NEW_YORK

feature

...

end-- class SANTA_BARBARA

 

 

Что предпринять, если LONDON и NEW_YORK имеют в своем составе компонент с именем, например, foo (нечто) ?

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

Вспомним класс TREE , порожденный от классов CELL и LIST , каждый из которых имеет компонент с именем item . Кроме того, оба класса имеют метод, названный put . Выбор каждого имени не случаен, и мы не хотим менять их в исходных классах лишь потому, что кому-то пришла идея объединить эти классы в дерево.Что делать? Исходный код классов LONDON и NEW_YORK может быть недоступен; или на его исправления может быть наложен запрет; а при отсутствии такого запрета, возможно, вам не захочется ничего менять, поскольку LONDON написан не вами, и выход новой версии класса заставит все начинать с нуля. Наконец, самое главное, принцип Открыт-Закрыт не разрешает исправлять модули при их повторном использовании.

Всегда ошибочно обвинять в грехах своих родителей. Проблема конфликта имен возникла в самом классе. В нем должно найтись и решение.

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

 

class SANTA_BARBARA inherit

LONDON

rename foo as fog end

NEW_YORK

feature

...

end

 

 

Как внутри SANTA_BARBARA , так и во всех клиентах этого класса компонент LONDON с именем foo будет именоваться fog , а одноименный компонент NEW_YORK - просто foo . Клиенты LONDON , как и прежде, будут знать этот компонент под именем foo .

Этого достаточно для устранения конфликта (если других совпадений нет, а класс LONDON и класс NEW_YORK не содержат компонента с именем fog ). В противном случае можно переименовать компонент класса NEW_YORK :

 

class SANTA_BARBARA inherit

LONDON

rename foo as fog end

NEW_YORK

rename foo as zoo end

feature

...

end

 

 

Предложение rename следует за указанием имени родителя и предшествует любым выражениям redefine , если таковые имеются. Можно переименовать и несколько компонентов, как в случае:

 

class TREE [G] inherit

CELL [G]

rename item as node_item, put as put_right end

 

 

где устраняется конфликт между одноименными компонентами CELL и LIST . Компоненту CELL с именем item дается идентификатор node_item , аналогично и put переименовывается в put_right .