Использование исходной версии при переопределении

Использование исходной версии при переопределении

Рассмотрим некоторый класс, который переопределяет подпрограмму, унаследованную от родителя. Обычная схема переопределения состоит в том, чтобы выполнить все, что делает исходная версия, предпослав ей или поместив за ней некоторые специальные действия.

Например, класс BUTTON, наследник класса WINDOW, может переопределить компонент display, рисующий кнопку, так чтобы вначале рисовалось окно, а затем появлялась рамка:

class BUTTON inherit

WINDOW

redefine display end

feature -- Вывод

display is

-- Изобразить как кнопку.

do

"Изобразить как нормальное окно"; -- См. ниже

draw_border

end

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

end

где draw_border - это процедура нового класса. Для того чтобы "Изобразить как нормальное окно", нужно вызвать исходную версию display, технически известную как precursor (предшественник) процедуры draw_border.

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

Precursor

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

Поэтому в последнем примере часть "Изобразить как нормальное окно" можно записать просто как

Precursor

Это будет означать вызов исходной версии этой процедуры из класса WINDOW, допустимый при переопределении процедуры классом-наследником WINDOW. Precursor - это зарезервированное имя сущности такое же, как Result или Current, и оно так же пишется курсивом с заглавной первой буквой.

В данном примере переопределяемый компонент является процедурой и поэтому вызов конструкции Precursor - это команда. Этот же вызов может участвовать при переопределении функции в выражении:

some_query (n: INTEGER): INTEGER is

-- Значение, возвращаемое версией родителя, если оно

-- положительно, иначе ноль

do

Result := (Precursor (n)).max (0)

end

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

Заметим, что использование конструкции Precursor не делает компонент-предшественник компонентом данного класса, компонентом является только его переопределенная версия. (В частности, предшествующая версия может не удовлетворять новому инварианту.) Целью конструкции является облегчение переопределения в случае, когда новая версия включает старую.

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