Пример повышенной сложности

We use cookies. Read the Privacy and Cookie Policy

Пример повышенной сложности

Вот более сложный пример применения разных аспектов дублируемого наследования.

Проблема, близкая по духу нашему примеру, возникла из интересного обсуждения в основной книге по C++ [Stroustrup 1991].

Рассмотрим класс WINDOW с процедурой display и двумя наследниками: WINDOW_WITH_BORDER и WINDOW_WITH_MENU. Эти классы описывают абстрактные окна, первое из них имеет рамку, а второе поддерживает меню. Переопределяя display, каждый класс выводит на экран стандартное окно, а затем добавляет к нему рамку (в первом случае) и меню (во втором).

Опишем окно с рамкой и с поддержкой меню. В результате мы породим класс WINDOW_WITH_BORDER_AND_MENU.

Рис. 15.24.  Варианты окна

Переопределим метод display в новом классе; новая версия вначале вызывает исходную, затем строит рамку, а потом строит меню. Исходный класс WINDOW имеет вид:

class WINDOW feature

display is

-- Отобразить окно (общий алгоритм)

do

...

end

... Другие компоненты ...

end

Наследник WINDOW_WITH_BORDER осуществляет вызов родительской версии display и затем отображает рамку. В дублируемом наследовании нет необходимости, достаточно воспользоваться механизмом Precursor:

class WINDOW_WITH_BORDER inherit

WINDOW

redefine display end

feature -- Output

display is

-- Рисует окно и его рамку.

do

Precursor

draw_border

end

feature {NONE} -- Implementation

draw_border is do ... end

...

end

Обратите внимание на процедуру draw_border, рисующую рамку окна. Она скрыта от клиентов класса WINDOW_WITH_BORDER (экспорт классу NONE), поскольку для них вызов draw_border не имеет смысла. Класс WINDOW_WITH_MENU аналогичен:

class WINDOW_WITH_MENU inherit

WINDOW

redefine display end

feature -- Output

display is

-- Рисует окно и его меню.

do

Precursor

draw_menu

end

feature {NONE} -- Implementation

draw_menu is do ... end

...

end

Осталось описать общего наследника WINDOW_WITH_BORDER_AND_MENU этих двух классов, дублируемого потомка WINDOW. Предпримем первую попытку:

indexing

WARNING: "Первая попытка - версия не будет работать корректно!"

class WINDOW_WITH_BORDER_AND_MENU inherit

WINDOW_WITH_BORDER

redefine display end

WINDOW_WITH_MENU

redefine display end

feature

display is

-- Рисует окно,его рамку и меню.

do

Precursor {WINDOW_WITH_BORDER}

Precursor {WINDOW_WITH_MENU}

end

...

end

Заметьте: при каждом обращении к Precursor мы вынуждены называть имя предка. Каждый предок имеет собственный компонент display, переопределенный под тем же именем.

Впрочем, как замечает Страуструп, это решение некорректно: версии родителей дважды вызывают исходную версию display класса WINDOW, что приведет к появлению "мусора" на экране. Для исправления ситуации добавим еще один класс, получив тройку наследников класса WINDOW:

indexing

note: "Это корректная версия"

class WINDOW_WITH_BORDER_AND_MENU inherit

WINDOW_WITH_BORDER

redefine

display

export {NONE}

draw_border

end

WINDOW_WITH_MENU

redefine

display

export {NONE}

draw_menu

end

WINDOW

redefine display end

feature

display is

-- Рисует окно,его рамку и меню.

do

Precursor {WINDOW}

draw_border

draw_menu

end

...

end

Заметьте, что компоненты draw_border и draw_menu в новом классе являются скрытыми, поскольку мы не видим причин, по которым клиенты WINDOW_WITH_BORDER_AND_MENU могли бы их вызывать непосредственно.

Несмотря на активное применение дублируемого наследования, класс переопределяет все унаследованные им варианты display, что делает выражения select ненужными. В этом состоит преимущество спецификатора Precursor в сравнении с репликацией компонентов.

Неплохим тестом на понимание дублируемого наследования станет решение этой задачи без применения Precursor, путем репликации компонентов промежуточных классов. При этом, разумеется, вам понадобится select (см. упражнение 15.10).

В полученном варианте класса присутствует лишь совместное использование, но не репликация компонентов. Расширим пример Страуструпа: пусть WINDOW имеет запрос id (возможно, целого типа), направленный на идентификацию окон. Если идентифицировать любое окно только одним "номером", то id будет использоваться совместно, и нам не придется ничего менять. Если же мы хотим проследить историю окна, то экземпляр WINDOW_WITH_BORDER_AND_MENU будет иметь три id - независимых "номера". Новый текст класса комбинирует совместное использование и репликацию id (изменения в тексте класса помечены стрелками):

indexing

note: "Усложненная версия с независимыми id."

class WINDOW_WITH_BORDER_AND_MENU inherit

WINDOW_WITH_BORDER

rename

id as border_id

redefine

display

export {NONE}

draw_border

end

WINDOW_WITH_MENU

rename

id as menu_id

redefine

display

export {NONE}

draw_menu

end

WINDOW

rename

id as window_id

redefine

display

select

window_id

end

feature

.... Остальное, как ранее...

end

Обратите внимание на необходимость выбора (select) одного из вариантов id.