Литмир - Электронная Библиотека

      end;

      { реализация конструктора }

constructor TPerson.Init(aBearing: integer; const aName, aFam : string);

begin

      mBearing:= aBearing; mName:= aName; mFam:= aFam;

end;

В этом примере конструктор Init копирует три своих параметра в поля объекта. Теперь переменную-объект P можно инициализировать вызовом конструктора.

var P : TPerson;       { переменная-объект }

begin

      P.Init(1995, 'Мария', 'Рыбкина');

Так ни одно поле объекта не будет пропущено, – за этим присмотрит компилятор!

Вот пока все, что следует сказать об инкапсуляции. Приведенный ниже пример «P_61_2» демонстрирует объект типа TPerson: здесь описана его структура и реализация методов, а затем объявлены две переменные, выполнена их инициализация и распечатка полей.

      { P_61_2 Программа с применением объекта типа «человек» (персона) }

type TPerson = object

      mBearing : integer; { год рождения }

      mName : string;       { имя }

      mFam : string;       { фамилия }

      constructor Init(aBearing: integer; const aName, aFam : string);

      procedure Report;       { процедура распечатки объекта }

      end;

      {--- Реализация двух методов объекта ---}

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;

var P1, P2 : TPerson; { две переменных объектного типа }

begin       {--- Главная программа ---}

      P1.Init(1985, 'Иван', 'Грозный');

      P2.Init(1995, 'Мария', 'Рыбкина');

      P1.Report;

      P2.Report;

      Readln;

end.

Наследование

Кажется, что инкапсуляция не упростила программирование. Да, это так, если рассматривать её в отрыве от других механизмов ООП: наследования и полиморфизма. Выигрыш мы ощутим, когда в ход пойдут все рычаги.

Наследование даёт возможность создавать новые типы объектов на основе существующих. Вновь создаваемые типы объектов – потомки – приобретают в наследство поля и методы своих предков. И вдобавок могут содержать новые поля и методы, а также изменять унаследованные.

Например, взяв почти готовый объект – окно в библиотеке, – программист добавляет к нему свои поля, методы и получает другой тип окна, работающий схожим образом, но с учетом потребностей программиста. При этом ему не придется вникать в тонкости объекта-предка, достаточно ознакомиться лишь с несколькими основными методами (подобно тому, как пользователю телевизора хватает лишь нескольких кнопок). Не нужен даже исходный текст модуля с описанием объекта-предка!

И это не все. Постройка одних объектов на основе других формирует иерархию родственных объектов. С разными объектами в этой иерархии можно обращаться сходным образом, – это и есть полиморфизм. Буквальный перевод этого слова – «многоструктурность» – почти ничего не объясняет. Принципы наследования и полиморфизма легче понять на примере.

Приборостроение

Я знаю, чем напичкан ваш дом, – электрическими приборами. И простыми, такими, как лампочка или утюг. И сложными, – телевизор, стиральная машина, компьютер, наконец. Взглянем на них глазами программиста: любой такой прибор, выражаясь языком ООП, обладает, по крайней мере, двумя общими «методами» – включить и отключить. В разных приборах эти операции выполняются по-разному, но в целом они сходны. Можно сказать, что эти методы – общие свойства всех электроприборов.

Если бы приборы создавал программист, то построил бы их на базе общего предка – абстрактного (воображаемого) электрического прибора. Этот прибор обладал бы двумя методами: включить и отключить. На Паскале этот абстрактный электроприбор был бы объявлен так:

type Электроприбор = object

      procedure Включить; virtual;

      procedure Отключить; virtual;

      end;

Здесь встречаем новое «волшебное» словечко – VIRTUAL, что значит «воображаемый». Это ключевое слово Паскаля следует за объявлением тех методов объекта, которые разрешено изменять в его наследниках. Изменение метода в наследниках называют переопределением метода. Итак, слово VIRTUAL указывает компилятору, что в наследниках методы включения и отключения прибора могут быть изменены в соответствии с особенностями этих наследников. К примеру, лампочка и телевизор включаются по-разному.

Основав абстрактный электроприбор, построим на нём прибор «чисто конкретный», например, телевизор.

type Телевизор = object (Электроприбор)

      procedure Включить; virtual;

      procedure Отключить; virtual;

      procedure Выбрать_канал;

      procedure Настроить_громкость;

      procedure Настроить_яркость;

      end;

Поскольку телевизор порожден от электроприбора, название его предка – «электроприбор» – указано в скобках за ключевым словом OBJECT. Наследник обязан помнить о предке, ссылаться на него, иначе не получит своего наследства – полей и методов. Виртуальные методы включить и отключить объявлены в наследнике точно так же, но будут реализованы иначе. К ним добавлены ещё три метода, характерные именно для телевизора. Схожим образом строятся и другие «конкретные» электроприборы. В результате сформируется иерархия родственных объектов, показанная на рис. 153.

Песни о Паскале (СИ) - _222.jpg

Рис.153 – Иерархия электрических приборов

Гражданское строительство

Но все это лишь присказка, теперь испытаем наследование и полиморфизм в деле. Создадим на базе спроектированного ранее объекта TPerson (человек) два новых типа данных: военнослужащий (TMilitary) и гражданский чиновник (TCivil), иерархия этих типов изображена на рис. 154. Эти новые типы «людей» будут содержать дополнительные поля с характерной для наследников информацией. Вдобавок изменим конструктор Init и метод Report с тем, чтобы учесть наличие новых полей. Конструктор будет содержать дополнительный параметр, а процедура распечатки – выводить на экран ещё одно поле объекта.

Песни о Паскале (СИ) - _223.jpg

Рис.154 – Иерархия «человеческих» типов

Объявление

Начнем с военнослужащего, чем разнится он от простых смертных? Гордым воинским званием – от рядового до маршала. Для хранения воинского звания в объекте TMilitary добавим строковое поле mRank (Rank – звание). Ясно, что при создании объекта конструктором надо указать этот элемент. Добавим ещё один параметр конструктору объекта Init – параметр aRank, и тогда заголовок конструктора в объекте TMilitary станет таким.

constructor Init(aBearing: integer; const aName, aFam, aRank : string);

В новом конструкторе больше параметров, и работать он будет, в сравнении с предком, чуть иначе. Другими словами, в наследнике он переопределен. А если так, то где же волшебное слово VIRTUAL? Его здесь нет и не должно быть, поскольку конструктор виртуален по определению.

Теперь обратимся к процедуре распечатки Report. В наследнике она, кроме прочего, должна распечатать поле воинского звания, а значит, будет переопределена. Поэтому и объявлена виртуальной, причем и в наследнике TMilitary, и в его предке TPerson. Это необходимо, поскольку лишь виртуальный метод предка может быть виртуальным у наследника: виртуальность передается по наследству. С учетом всего сказанного, объявления типов TPerson и TMilitary теперь будут такими.

114
{"b":"596178","o":1}