mBearing := aBearing; mName := aName; mFam := aFam;
end;
procedure TPerson.Report;
begin
Writeln(mBearing:6, 'Фамилия: '+mFam:20, ' Имя: '+mName);
end;
var P1, P2 : TPerson; { две переменных объектного типа }
begin {--- Главная программа ---}
P1.Init(1985, 'Иван', 'Грозный');
P2.Init(1995, 'Мария', 'Рыбкина');
P1.Report;
P2.Report;
Readln;
end.
Кажется, что инкапсуляция не упростила программирование. Да, это так, если рассматривать её в отрыве от других механизмов ООП: наследования и полиморфизма. Выигрыш мы ощутим, когда в ход пойдут все рычаги.
Наследование даёт возможность создавать новые типы объектов на основе существующих. Вновь создаваемые типы объектов – потомки – приобретают в наследство поля и методы своих предков. И вдобавок могут содержать новые поля и методы, а также изменять унаследованные.
Например, взяв почти готовый объект – окно в библиотеке, – программист добавляет к нему свои поля, методы и получает другой тип окна, работающий схожим образом, но с учетом потребностей программиста. При этом ему не придется вникать в тонкости объекта-предка, достаточно ознакомиться лишь с несколькими основными методами (подобно тому, как пользователю телевизора хватает лишь нескольких кнопок). Не нужен даже исходный текст модуля с описанием объекта-предка!
И это не все. Постройка одних объектов на основе других формирует иерархию родственных объектов. С разными объектами в этой иерархии можно обращаться сходным образом, – это и есть полиморфизм. Буквальный перевод этого слова – «многоструктурность» – почти ничего не объясняет. Принципы наследования и полиморфизма легче понять на примере.
Я знаю, чем напичкан ваш дом, – электрическими приборами. И простыми, такими, как лампочка или утюг. И сложными, – телевизор, стиральная машина, компьютер, наконец. Взглянем на них глазами программиста: любой такой прибор, выражаясь языком ООП, обладает, по крайней мере, двумя общими «методами» – включить и отключить. В разных приборах эти операции выполняются по-разному, но в целом они сходны. Можно сказать, что эти методы – общие свойства всех электроприборов.
Если бы приборы создавал программист, то построил бы их на базе общего предка – абстрактного (воображаемого) электрического прибора. Этот прибор обладал бы двумя методами: включить и отключить. На Паскале этот абстрактный электроприбор был бы объявлен так:
type Электроприбор = object
procedure Включить;
procedure Отключить;
end;
Здесь встречаем новое «волшебное» словечко – VIRTUAL, что значит «воображаемый». Это ключевое слово Паскаля следует за объявлением тех методов объекта, которые разрешено изменять в его наследниках. Изменение метода в наследниках называют переопределением метода. Итак, слово VIRTUAL указывает компилятору, что в наследниках методы включения и отключения прибора могут быть изменены в соответствии с особенностями этих наследников. К примеру, лампочка и телевизор включаются по-разному.
Основав абстрактный электроприбор, построим на нём прибор «чисто конкретный», например, телевизор.
type Телевизор = object
procedure Включить;
procedure Отключить;
procedure Выбрать_канал;
procedure Настроить_громкость;
procedure Настроить_яркость;
end;
Поскольку телевизор порожден от электроприбора, название его предка – «электроприбор» – указано в скобках за ключевым словом OBJECT. Наследник обязан помнить о предке, ссылаться на него, иначе не получит своего наследства – полей и методов. Виртуальные методы включить и отключить объявлены в наследнике точно так же, но будут реализованы иначе. К ним добавлены ещё три метода, характерные именно для телевизора. Схожим образом строятся и другие «конкретные» электроприборы. В результате сформируется иерархия родственных объектов, показанная на рис. 153.