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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32
 
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;

Таким образом мы получили новые классы, основанные на базе класса THuman, имеющие те же самые поля FirstName, LastName, YearOfBirth, метод Age и т.д, но только с какими-то дополнительными своими полями (возможно и методами), которые характерны уже для данного класса.
[note]Родительский класс — класс, от которого был унаследован другой класс, перенявший описание и реализацию у первого. В нашем случае родительским классом является класс THuman.[/note]
[note]Дочерний класс — класс, который унаследовал описание и реализацию какого-либо родительского класса. Относительно родительского класса такой класс и называется дочерним. У нас это классы TTeacher и TStudent.[/note]
При обращении к классу TTeacher или TStudent, в нем будут доступны также все поля и методы из класса THuman, но с учетом областей доступа. Если существуют некоторые поля или методы у родительского класса в группе доступа private (и тем более strict private), то они будут невидимы при попытке к ним обратиться из дочернего класса. Хотя, использование родительскими методами этих приватных полей и методов, в том случае если к этим родительским методам есть доступ, ничем не ограничено, т.к. эти методы реализованы не в этом классе, а в родительском, где есть необходимый доступ. Интересно в данном случае обратить внимание на реализацию конструктора Create у классов TTeacher и TStudent:

1

2

3

4

5
 
constructor TStudent.Create(NewFirstName, NewLastName: string; NewClassNumber: byte);

begin

  inherited Create(NewFirstName, NewLastName);

  ClassNumber := NewClassNumber;

end;

Таким образом мы можем вызывать именно родительские методы, т.к. в данном случае метод TStudent.Create перекрывает THuman.Create. Ну и соответственно для такого вызова мы воспользуемся ключевым словом inherited. Сначала пишем inherited, а затем указываем название родительского метода и параметры. Будет вызван сначала конструктор родительского класса, который создаст объект в качестве THuman, а затем уже произойдет присвоение поля ClassNumber, характерное только для TStudent.
[tip]Немножечко отвлечемся от текущей темы, и рассмотрим более простой пример, когда у одного класса может существовать несколько конструкторов. Да, такое возможно, впрочем как и несколько деструкторов. Например, если они будут иметь разные названия. Если потребуется реализовать один конструктор B на базе другого A, уже существующего в этом классе, то для этого нужно всего-лишь вызвать другой конструктор A в нужном месте нашего нового конструктора B. Прямо как в предыдущем примере, только без слова inherited. Никаких присвоений делать не надо. Конструктор A выполнит создание объекта согласно тому, как это создание в нем реализовано. А затем уже в конструкторе B мы можем, к примеру, присвоить другие поля. Можем даже рассмотреть такой пример. Добавив в наш класс TStudent еще один конструктор:

 
1
 
constructor CreateWithFirstMark(NewFirstName, NewLastName: string; NewClassNumber: byte; FirstMark: byte);

Реализация нового метода тогда будет выглядеть так:

1

2

3

4

5

6

7
 
constructor TStudent.CreateWithFirstMark(NewFirstName, NewLastName: string; NewClassNumber: byte; FirstMark: byte);

begin

  Create(NewFirstName, NewLastName, NewClassNumber); // Вызываем сначала базовый конструктор

  // Он в свою очередь у нас еще создаст объект в качестве THuman и присвоит ClassNumber

  // Ну а теперь присвоим первую оценку ученику

  Marks[0] := FirstMark;

end;


Фактически, когда в TStudent.Create мы приписали слово inherited, мы просто хотели обратиться конкретно к родительскому конструктору. Но так делать следует только в том случае, когда какое-то имя какого-либо метода из текущего класса перекрывает метод родительского класса. Мы и указываем inherited, чтобы вызвать именно метод родительского класса. Но в остальных случаях, когда имя метода (или поля) не перекрывается, то метод (или поле) родительского класса можно вызвать также, словно он был объявлен в дочернем классе (главное, чтобы этот метод или поле не были закрыты группой доступа в родительском классе):

1

2

3

4

5

6

7

8
 
constructor TStudent.Create(NewFirstName, NewLastName: string; NewClassNumber: byte);

begin

  inherited Create(NewFirstName, NewLastName);

  ClassNumber := NewClassNumber;



  YearOfBirth := 1996; // Обращение к полю родительского класса

  ShowMessage('Ученику '+NewFirstName+' '+NewLastName+' '+IntToStr(Age)+' лет'); // Обращение к методу Age родительского класса

end;

Т.е. вы видите, как легко можно обратиться к полям и методам родительского класса.

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

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

Категория: delphi 7 | Добавил: ghost_mod (14.09.2016)
Просмотров: 1263 | Рейтинг: 0.0/0
Всего комментариев: 0
Имя *:
Email:
Подписка:1
Код *: