Delphi. Урок 22. Наследование классов

Уроки для начинающих   25 октября 2013  Автор статьи: admin 
geekbrains.ru/

Наследование — это еще одна очень важная парадигма в объектно-ориентированном программировании, которая применяется программистами очень широко. Итак, что же представляет собой наследование? Для того чтобы ответить на этот вопрос, перейдем к несложному примеру. Предположим, у нас существует три класса: человек, ученик и преподаватель. Согласитесь, что класс «человек» на уровень более общий, чем классы «ученик» и «преподаватель», т.е. «ученик» и «преподаватель» — это по сути и те же экземпляры класса «человек», только имеющие дополнительные, характерные для них самих, методы и поля. Как раз для того, чтобы один класс мог перенять поля и методы у другого и содержать набор собственных полей и методов были придуманы принципы наследования классов.
[cc lang=»delphi»]
type

THuman = class
public
FirstName, LastName: string;
YearOfBirth: integer;
Height: integer;

function Age: byte;

constructor Create(NewFirstName, NewLastName: string);
end;

TTeacher = class(THuman) // В скобках после ключевого слова class
// указывается родительский класс, методы и поля которого
// будут также доступны из этого класса TTeacher
public
Subject: string;

constructor Create(NewFirstName, NewLastName, NewSubject: string);
end;

TStudent = class(THuman) // В скобках после ключевого слова class
// указывается родительский класс, методы и поля которого
// будут также доступны из этого класса TStudent
public
ClassNumber: byte;
Marks: array [0..10] of byte;
Teachers: array [0..10] of TTeacher;

constructor Create(NewFirstName, NewLastName: string; NewClassNumber: byte);
end;
[/cc]
Таким образом мы получили новые классы, основанные на базе класса THuman, имеющие те же самые поля FirstName, LastName, YearOfBirth, метод Age и т.д, но только с какими-то дополнительными своими полями (возможно и методами), которые характерны уже для данного класса.
[note]Родительский класс — класс, от которого был унаследован другой класс, перенявший описание и реализацию у первого. В нашем случае родительским классом является класс THuman.[/note]
[note]Дочерний класс — класс, который унаследовал описание и реализацию какого-либо родительского класса. Относительно родительского класса такой класс и называется дочерним. У нас это классы TTeacher и TStudent.[/note]
При обращении к классу TTeacher или TStudent, в нем будут доступны также все поля и методы из класса THuman, но с учетом областей доступа. Если существуют некоторые поля или методы у родительского класса в группе доступа private (и тем более strict private), то они будут невидимы при попытке к ним обратиться из дочернего класса. Хотя, использование родительскими методами этих приватных полей и методов, в том случае если к этим родительским методам есть доступ, ничем не ограничено, т.к. эти методы реализованы не в этом классе, а в родительском, где есть необходимый доступ. Интересно в данном случае обратить внимание на реализацию конструктора Create у классов TTeacher и TStudent:
[cc lang=»delphi»]constructor TStudent.Create(NewFirstName, NewLastName: string; NewClassNumber: byte);
begin
inherited Create(NewFirstName, NewLastName);
ClassNumber := NewClassNumber;
end;[/cc]
Таким образом мы можем вызывать именно родительские методы, т.к. в данном случае метод TStudent.Create перекрывает THuman.Create. Ну и соответственно для такого вызова мы воспользуемся ключевым словом inherited. Сначала пишем inherited, а затем указываем название родительского метода и параметры. Будет вызван сначала конструктор родительского класса, который создаст объект в качестве THuman, а затем уже произойдет присвоение поля ClassNumber, характерное только для TStudent.
[tip]Немножечко отвлечемся от текущей темы, и рассмотрим более простой пример, когда у одного класса может существовать несколько конструкторов. Да, такое возможно, впрочем как и несколько деструкторов. Например, если они будут иметь разные названия. Если потребуется реализовать один конструктор B на базе другого A, уже существующего в этом классе, то для этого нужно всего-лишь вызвать другой конструктор A в нужном месте нашего нового конструктора B. Прямо как в предыдущем примере, только без слова inherited. Никаких присвоений делать не надо. Конструктор A выполнит создание объекта согласно тому, как это создание в нем реализовано. А затем уже в конструкторе B мы можем, к примеру, присвоить другие поля. Можем даже рассмотреть такой пример. Добавив в наш класс TStudent еще один конструктор:
[cc lang=»delphi»]constructor CreateWithFirstMark(NewFirstName, NewLastName: string; NewClassNumber: byte; FirstMark: byte);[/cc]
Реализация нового метода тогда будет выглядеть так:
[cc lang=»delphi»]constructor TStudent.CreateWithFirstMark(NewFirstName, NewLastName: string; NewClassNumber: byte; FirstMark: byte);
begin
Create(NewFirstName, NewLastName, NewClassNumber); // Вызываем сначала базовый конструктор
// Он в свою очередь у нас еще создаст объект в качестве THuman и присвоит ClassNumber
// Ну а теперь присвоим первую оценку ученику
Marks[0] := FirstMark;
end;[/cc][/tip]
Фактически, когда в TStudent.Create мы приписали слово inherited, мы просто хотели обратиться конкретно к родительскому конструктору. Но так делать следует только в том случае, когда какое-то имя какого-либо метода из текущего класса перекрывает метод родительского класса. Мы и указываем inherited, чтобы вызвать именно метод родительского класса. Но в остальных случаях, когда имя метода (или поля) не перекрывается, то метод (или поле) родительского класса можно вызвать также, словно он был объявлен в дочернем классе (главное, чтобы этот метод или поле не были закрыты группой доступа в родительском классе):
[cc lang=»delphi»]constructor TStudent.Create(NewFirstName, NewLastName: string; NewClassNumber: byte);
begin
inherited Create(NewFirstName, NewLastName);
ClassNumber := NewClassNumber;

YearOfBirth := 1996; // Обращение к полю родительского класса
ShowMessage(‘Ученику ‘+NewFirstName+’ ‘+NewLastName+’ ‘+IntToStr(Age)+’ лет’); // Обращение к методу Age родительского класса
end;[/cc]
Т.е. вы видите, как легко можно обратиться к полям и методам родительского класса.

Группа доступа protected

Как я уже обещал в предыдущем уроке, при изучении принципов наследования классов, у нас появится еще одна группа доступа. Называется она protected. Она схожа с группой доступа private, и единственное различие состоит в том, что перечисленные в этой группе доступа методы и поля будут доступны еще и из дочерних классов. Т.е. только внутри класса, в котором эти методы и поля были объявлены, а также в дочерних классах.
Ну и соответственно существует еще одна группа доступа «strict protected», видимость которой не распространяется в текущем модуле, т.е. так же как и «strict private» по сравнению с обычным «private».
Ну и в очередной раз говорить, что разобранная в данном уроке парадигма ООП очень полезна и открывает довольно много возможностей, бессмысленно — это и так понятно. К тому же, опять-таки, она крайне удобна в командной работе, т.к. позволяет программистам, к примеру, договориться о реализации родительского класса, а затем уже каждый сможет использовать эту реализацию класса и расширять ее в дочерних классах, имея при этом такие возможности.

Научиться программировать

  • на Delphi

  • на Java

  • на C++

geekbrains.ru/
geekbrains.ru/