Песни о Паскале
Шрифт:
Если бы приборы создавал программист, то построил бы их на базе общего предка – абстрактного (воображаемого) электрического прибора. Этот прибор обладал бы двумя методами: включить и отключить. На Паскале этот абстрактный электроприбор был бы объявлен так:
type Электроприбор = object
procedure Включить; virtual;
procedure Отключить; virtual;
end;
Здесь встречаем новое «волшебное» словечко – VIRTUAL, что значит «воображаемый». Это ключевое слово Паскаля следует за объявлением тех методов объекта, которые разрешено изменять в его наследниках. Изменение
Основав абстрактный электроприбор, построим на нём прибор «чисто конкретный», например, телевизор.
type Телевизор = object (Электроприбор)
procedure Включить; virtual;
procedure Отключить; virtual;
procedure Выбрать_канал;
procedure Настроить_громкость;
procedure Настроить_яркость;
end;
Поскольку телевизор порожден от электроприбора, название его предка – «электроприбор» – указано в скобках за ключевым словом OBJECT. Наследник обязан помнить о предке, ссылаться на него, иначе не получит своего наследства – полей и методов. Виртуальные методы включить и отключить объявлены в наследнике точно так же, но будут реализованы иначе. К ним добавлены ещё три метода, характерные именно для телевизора. Схожим образом строятся и другие «конкретные» электроприборы. В результате сформируется иерархия родственных объектов, показанная на рис. 153.
Рис.153 – Иерархия электрических приборов
Гражданское строительство
Но все это лишь присказка, теперь испытаем наследование и полиморфизм в деле. Создадим на базе спроектированного ранее объекта TPerson (человек) два новых типа данных: военнослужащий (TMilitary) и гражданский чиновник (TCivil), иерархия этих типов изображена на рис. 154. Эти новые типы «людей» будут содержать дополнительные поля с характерной для наследников информацией. Вдобавок изменим конструктор Init и метод Report с тем, чтобы учесть наличие новых полей. Конструктор будет содержать дополнительный параметр, а процедура распечатки – выводить на экран ещё одно поле объекта.
Рис.154 – Иерархия «человеческих» типов
Объявление
Начнем с военнослужащего, чем разнится он от простых смертных? Гордым воинским званием – от рядового до маршала. Для хранения воинского звания в объекте TMilitary добавим строковое поле mRank (Rank – звание). Ясно, что при создании объекта конструктором надо указать этот элемент. Добавим ещё один параметр конструктору объекта Init – параметр aRank, и тогда заголовок конструктора в объекте TMilitary станет таким.
constructor Init(aBearing: integer; const aName, aFam, aRank : string);
В
Теперь обратимся к процедуре распечатки Report. В наследнике она, кроме прочего, должна распечатать поле воинского звания, а значит, будет переопределена. Поэтому и объявлена виртуальной, причем и в наследнике TMilitary, и в его предке TPerson. Это необходимо, поскольку лишь виртуальный метод предка может быть виртуальным у наследника: виртуальность передается по наследству. С учетом всего сказанного, объявления типов TPerson и TMilitary теперь будут такими.
TPerson = object
mBearing : integer; { год рождения }
mName : string; { имя }
mFam : string; { фамилия }
constructor Init(aBearing: integer; const aName, aFam : string);
procedure Report; virtual;
end;
TMilitary = object (TPerson)
mRank : string; { воинское звание }
constructor Init(aBearing: integer; const aName, aFam,
aRank : string);
procedure Report; virtual;
end;
Подытожим все изменения. В предке TPerson процедура Report стала виртуальной. В наследнике TMilitary добавлено поле mRank, а также изменены два метода: конструктор и процедура Report.
«Отселение» в отдельный модуль
Настало время реализовать методы наследника. Но прежде, чем взяться за это, совершим одно полезное дельце – переместим объект-предок TPerson в отдельный модуль. Именно так поступают профессионалы, создавая библиотеки объектов. Порядок создания программного модуля подробно изложен в главе 59, вкратце я напомню основные шаги.
Итак, создайте новый файл, перенесите туда через буфер обмена объявление типа TPerson и реализацию его методов. Объявление объекта разместите в секции INTERFACE модуля, а реализацию – в секции IMPLEMENTATION. И не забудьте объявить виртуальной процедуру Report. Дайте модулю имя PERSON и сохраните под именем «PERSON.PAS». У вас получится файл, показанный ниже. В нём объявлен ещё один тип данных – указатель на объект PPerson, но к нему обратимся позже.
unit Person; { Модуль, содержащий описание и реализацию объекта «ЧЕЛОВЕК» }
interface
type PPerson = ^TPerson; { указатель на объект «ЧЕЛОВЕК» }
TPerson = object
mBearing : integer; { год рождения }
mName : string; { имя }
mFam : string; { фамилия }
constructor Init(aBearing: integer; const aName, aFam : string);
procedure Report; virtual;
end;
implementation
{--- Реализация объекта «ЧЕЛОВЕК» ---}
constructor TPerson.Init(aBearing: integer; const aName, aFam : string);
begin
mBearing := aBearing;
mName := aName;
mFam := aFam;
end;
procedure TPerson.Report;
begin
Writeln(mBearing:6, 'Фамилия: '+mFam:20, ' Имя: '+mName);
end;
end.
Теперь то, что переехало в модуль Person, из первичного файла проекта удалим, и добавим в начале программы ссылку для импорта модуля.
USES Person;