Основы программирования — второй семестр 08-09; Михалкович С.С.; VI часть — различия между версиями

Материал из Вики ИТ мехмата ЮФУ
Перейти к: навигация, поиск
(Наследование на примере Student - SeniorStudent)
(Наследование на примере Student - SeniorStudent)
Строка 200: Строка 200:
 
end;
 
end;
 
</source>
 
</source>
 +
 +
<xh4> Производный класс SeniorStudent </xh4>
 +
<tt>SeniorStudent</tt> — студент старших курсов.  <br />
 +
Помимо имени, возраста, курса и группы:
 +
* у него будет научный руководитель (Advisor)
 +
* он будет знать тему курсовой работы, которую получает от своего научного руководителя
 +
* и для него будет известно, какую он получает образовательную степень (бакалавра, специалиста или магистра)
 +
 +
Будем считать, что класс преподавателя у нас уже есть, и есть метод <tt>SayCourseWorkTheme(MyStudent: SeniorStudent): string</tt>. <br />
 +
Или сделаем пока заглушку:
 +
{{Hider
 +
|title = Teacher
 +
|content =
 +
<source lang="Delphi">
 +
type
 +
  /// Преподаватель
 +
  Teacher = class
 +
  private
 +
    /// Имя
 +
    fName: string;
 +
   
 +
    procedure SetName(Name: string);
 +
 
 +
  public
 +
    /// Имя — только на чтение
 +
    property Name: string read fName;
 +
   
 +
    constructor Create(Name: string);
 +
   
 +
    /// Возвращает тему курсовой работы для студента MyStudent
 +
    function SayCourseWorkTheme(MyStudent: SeniorStudent): string;
 +
   
 +
    /// Выводит данные по преподавателю
 +
    procedure Print;
 +
  end;
 +
 +
implementation
 +
 +
procedure Teacher.SetName(Name: string);
 +
begin
 +
  if Name <> '' then
 +
    fName := Name
 +
  else
 +
    raise new Exception(
 +
    'Попытка присвоить преподавателю пустое имя!');
 +
end;
 +
 +
constructor Teacher.Create(Name: string);
 +
begin
 +
  SetName(Name);
 +
end;
 +
 +
function Teacher.SayCourseWorkTheme(MyStudent: SeniorStudent): string;
 +
begin
 +
  result := 'Тема курсовой работы';
 +
end;
 +
 +
procedure Teacher.Print;
 +
begin
 +
  WriteFormat(
 +
  'Имя: {0}  ',
 +
  fName);
 +
end;
 +
</source>}}
 +
 +
Приступим к реализации <tt>SeniorStudent</tt>. <br />
 +
Для начала нам понадобится вспомогательный тип <tt>EduModelType</tt> (образовательная степень), для лучшего использования которого напишем две функции: <br />
 +
<tt>function MaxCourseByEduModel(eduModel: EduModelType): integer</tt> — возвращающую максимальный курс, соответствующий образовательной степени eduModel <br />
 +
и <tt>function StringEduModel(eduModel: EduModelType): string</tt> — возвращающую строковое представление образовательной степени:
 +
<source lang="Delphi">
 +
type
 +
  /// Образовательная степень (Бакалавр, Специалист, Магистр)
 +
  EduModelType = (Bachelor, Specialist, Magister);
 +
 
 +
/// Возвращает максимальный курс, соответствующий образовательной степени eduModel
 +
function MaxCourseByEduModel(eduModel: EduModelType): integer;
 +
 
 +
/// Возвращает строковое представление образовательной степени
 +
function StringEduModel(eduModel: EduModelType): string;
 +
 +
implementation
 +
 +
function MaxCourseByEduModel(eduModel: EduModelType): integer;
 +
begin
 +
  case eduModel of
 +
    Bachelor: result := 4;
 +
    Specialist: result := 5;
 +
    Magister: result := 6;
 +
  end;
 +
end;
 +
 +
function StringEduModel(eduModel: EduModelType): string;
 +
begin
 +
  case eduModel of
 +
    Bachelor: result := 'Бакалавр';
 +
    Specialist: result := 'Специалист';
 +
    Magister: result := 'Магистр';
 +
  end;
 +
end;
 +
</source>
 +
 +
Теперь напишем интерфейс нашего старшекурсника:
 +
<source lang="Delphi">
 +
  /// Студент старших курсов
 +
  SeniorStudent = class (Student)
 +
  private
 +
    fAdvisor: Teacher;
 +
    fEduModel: EduModelType;
 +
    fCourseWorkTheme: string;
 +
   
 +
    procedure SetAdvisor(Advisor: Teacher);
 +
    procedure SetEduModel(EduModel: EduModelType);
 +
   
 +
    /// Устанавливает тему курсовой работы, получая её от научного руководителя
 +
    procedure GetCourseWorkTheme;
 +
   
 +
  public
 +
    /// Научный руководитель — только на чтение
 +
    property Advisor: Teacher read fAdvisor;
 +
    /// Образовательная степень — только на чтение
 +
    property EduModel: EduModelType read fEduModel;
 +
    /// Тема курсовой работы — только на чтение
 +
    property CourseWorkTheme: string read fCourseWorkTheme;
 +
   
 +
    constructor Create(Name: string; Age, Course, Group: integer;
 +
      Advisor: Teacher; EduModel: EduModelType);
 +
   
 +
    procedure NextCourse;
 +
   
 +
    procedure Print;
 +
    procedure Println;
 +
  end;
 +
</source>
 +
 +
Как реализовать сеттеры, <tt>GetCourseWorkTheme</tt> и NextCourse мы знаем:
 +
<source lang="Delphi">
 +
procedure SeniorStudent.SetAdvisor(Advisor: Teacher);
 +
begin
 +
  fAdvisor := Advisor;
 +
end;
 +
 +
procedure SeniorStudent.SetEduModel(EduModel: EduModelType);
 +
begin
 +
  fEduModel := EduModel;
 +
end;
 +
 +
procedure SeniorStudent.GetCourseWorkTheme;
 +
begin
 +
  fCourseWorkTheme := fAdvisor.SayCourseWorkTheme(self);
 +
end;
 +
 +
procedure SeniorStudent.NextCourse;
 +
begin
 +
  var maxCourse := MaxCourseByEduModel(EduModel);
 +
  if fCourse < maxCourse then
 +
  begin
 +
    fCourse += 1;
 +
    IncAge;
 +
  end
 +
  else
 +
    raise new Exception(
 +
    'Выход за границы диапазона допустимых курсов [' +
 +
    MIN_COURSE.ToString + '..' + maxCourse.ToString + ']!');
 +
end;
 +
</source>
 +
 +
'''Замечание.''' В процессе разработки возникла проблема доступа к приватным полям предка (fCourse) <br />
 +
Если Student и SeniorStudent реализованы в одном модуле, то такой проблемы нет и доступ возможен, однако, если они они реализованы в разных модулях, то данный код не откомпилируется. <br />
 +
Исправим это в дальнейшем, а пока будем считать, что классы реализованы в одном модуле.
 +
 +
Заметим, что процедура <tt>NextCourse</tt> была полностью переопределена в потомке. <br />
 +
Но, как правило, одноименный метод в потомке не переписывается полностью, а вызывает соответствующий метод предка.
 +
 +
Переопределяющий метод называется также '''заменяющим'''.
 +
 +
Для вызова в методе <tt>Method</tt> замещенного метода предка используется конструкция
 +
'''inherited''' Method
 +
'''Inherited''' — значит ''унаследованный''.
 +
 +
Воспользуемся этим при реализации оставшихся методов:
 +
<source lang="Delphi">
 +
constructor SeniorStudent.Create(Name: string; Age, Course, Group: integer;
 +
  Advisor: Teacher; EduModel: EduModelType);
 +
begin
 +
  inherited Create(Name, Age, Course, Group);
 +
  fAdvisor := Advisor;
 +
  fEduModel := EduModel;
 +
  GetCourseWorkTheme;
 +
end;
 +
 +
procedure SeniorStudent.Print;
 +
begin
 +
  inherited Print;
 +
 
 +
  write('Преподаватель: '); 
 +
  fAdvisor.Print;
 +
  writeFormat('Тема курсовой работы: «{0}»  ', CourseWorkTheme);
 +
 
 +
  writeFormat('Образовательная степень: {0}', StringEduModel(EduModel));
 +
end;
 +
 +
procedure SeniorStudent.Println;
 +
begin
 +
  Print;
 +
  writeln();
 +
end;
 +
</source>
 +
 +
 +
{{Hider
 +
|title = Полный текст текущего модуля
 +
|content =
 +
<source lang="Delphi">
 +
unit University;
 +
 +
interface
 +
uses System;
 +
 +
const 
 +
  /// Минимальный допустимый возраст студента
 +
  MIN_AGE = 1;
 +
  /// Максимальный допустимый возраст студента
 +
  MAX_AGE = 120;
 +
 
 +
  /// Минимальный возможный курс
 +
  MIN_COURSE = 1;
 +
 
 +
// ============================================ Student =========================================== 
 +
const
 +
  /// Максимальный возможный курс
 +
  MAX_COURSE = 4;
 +
 +
type
 +
  /// Студент
 +
  Student = class
 +
  private
 +
    /// Имя
 +
    fName: string;
 +
    /// Возраст
 +
    fAge: integer;
 +
    /// Курс
 +
    fCourse: integer;
 +
    /// Группа
 +
    fGroup: integer;
 +
   
 +
    procedure SetName(Name: string);
 +
    procedure SetAge(Age: integer);
 +
    procedure SetCourse(Course: integer);
 +
    procedure SetGroup(Group: integer);
 +
   
 +
    procedure IncAge;
 +
   
 +
  public
 +
    // --------------------------------- Свойства --------------------------------
 +
    /// Имя — только на чтение
 +
    property Name: string read fName;
 +
    /// Возраст — только на чтение
 +
    property Age: integer read fAge;
 +
    /// Курс — только на чтение
 +
    property Course: integer read fCourse;
 +
    /// Группа — только на чтение
 +
    property Group: integer read fGroup;
 +
   
 +
    /// <summary>
 +
    /// Создает нового студента
 +
    /// </summary>
 +
    /// <param name="Name">Имя (пустое недопустимо)</param>
 +
    /// <param name="Age">Возраст (отрицательный или больший MAX_AGE недопустим)</param>
 +
    /// <param name="Course">Курс (отрицательный или больший MAX_COURSE недопустим)</param>
 +
    /// <param name="Group">Группа (отрицательная недопустима)</param>
 +
    constructor Create(Name: string; Age, Course, Group: integer);
 +
   
 +
    /// Переводит студента на следующий курс, если он меньше MAX_COURSE
 +
    procedure NextCourse;
 +
   
 +
    procedure Print;
 +
    procedure Println;
 +
  end;
 +
 
 +
// ========================================== EduModelType ======================================== 
 +
type
 +
  /// Образовательная степень (Бакалавр, Специалист, Магистр)
 +
  EduModelType = (Bachelor, Specialist, Magister);
 +
 
 +
/// Возвращает максимальный курс, соответствующий образовательной степени eduModel
 +
function MaxCourseByEduModel(eduModel: EduModelType): integer;
 +
 
 +
/// Возвращает строковое представление образовательной степени
 +
function StringEduModel(eduModel: EduModelType): string;
 +
 
 +
// ============================================ Teacher ===========================================
 +
type
 +
  SeniorStudent = class; // предописание класса
 +
 
 +
  /// Преподаватель
 +
  Teacher = class
 +
  private
 +
    /// Имя
 +
    fName: string;
 +
 +
    procedure SetName(Name: string);
 +
 
 +
  public
 +
    /// Имя — только на чтение
 +
    property Name: string read fName;
 +
   
 +
    /// <summary>
 +
    /// Создает нового преподавателя
 +
    /// </summary>
 +
    /// <param name="Name">Имя (пустое недопустимо)</param>
 +
    constructor Create(Name: string);
 +
   
 +
    /// Возвращает тему курсовой работы для студента MyStudent
 +
    function SayCourseWorkTheme(MyStudent: SeniorStudent): string;
 +
   
 +
    procedure Print;
 +
    procedure Println;
 +
  end;
 +
 
 +
// ========================================= SeniorStudent ======================================== 
 +
  /// Студент старших курсов
 +
  SeniorStudent = class (Student)
 +
  private
 +
    /// Научный руководитель
 +
    fAdvisor: Teacher;
 +
    /// Образовательная степень
 +
    fEduModel: EduModelType;
 +
    /// Тема курсовой работы
 +
    fCourseWorkTheme: string;
 +
   
 +
 +
    procedure SetAdvisor(Advisor: Teacher);
 +
    procedure SetEduModel(EduModel: EduModelType);
 +
   
 +
    /// Устанавливает тему курсовой работы, получая её от научного руководителя
 +
    procedure GetCourseWorkTheme;
 +
   
 +
  public
 +
    /// Научный руководитель — только на чтение
 +
    property Advisor: Teacher read fAdvisor;
 +
    /// Образовательная степень — только на чтение
 +
    property EduModel: EduModelType read fEduModel;
 +
    /// Тема курсовой работы — только на чтение
 +
    property CourseWorkTheme: string read fCourseWorkTheme;
 +
   
 +
    /// <summary>
 +
    /// Создает нового студента старших курсов
 +
    /// </summary>
 +
    /// <param name="Name">Имя (пустое недопустимо)</param>
 +
    /// <param name="Age">Возраст (отрицательный или больший MAX_AGE недопустим)</param>
 +
    /// <param name="Course">Курс (отрицательный или больший MAX_COURSE недопустим)</param>
 +
    /// <param name="Group">Группа (отрицательная недопустима)</param>
 +
    /// <param name="Advisor">Научный руководитель</param>
 +
    /// <param name="EduModel">Образовательная степень (бакалавр, специалист, магистр)</param>
 +
    constructor Create(Name: string; Age, Course, Group: integer;
 +
      Advisor: Teacher; EduModel: EduModelType);
 +
   
 +
    procedure NextCourse;
 +
   
 +
    procedure Print;
 +
    procedure Println;
 +
  end;
 +
 
 +
implementation
 +
 +
// ============================================ Student ===========================================
 +
 +
procedure Student.SetName(Name: string);
 +
begin
 +
  if Name <> '' then
 +
    fName := Name
 +
  else
 +
    raise new Exception(
 +
    'Попытка присвоить студенту пустое имя!');
 +
end;
 +
 +
procedure Student.SetAge(Age: integer);
 +
begin
 +
  if (Age >= MIN_AGE) and (Age <= MAX_AGE) then
 +
    fAge := Age
 +
  else
 +
    raise new Exception(
 +
    'Выход за границы диапазона допустимого возраста [' +
 +
    MIN_AGE.ToString + '..' + MAX_AGE.ToString + ']!');
 +
end;
 +
 +
procedure Student.SetCourse(Course: integer);
 +
begin
 +
  if (Course >= MIN_COURSE) and (Course <= MAX_COURSE) then
 +
    fCourse := Course
 +
  else
 +
    raise new Exception(
 +
    'Выход за границы диапазона допустимых курсов [' +
 +
    MIN_COURSE.ToString + '..' + MAX_COURSE.ToString + ']!');
 +
end;
 +
 +
procedure Student.SetGroup(Group: integer);
 +
begin
 +
  if (Group > 0) then
 +
    fGroup := Group
 +
  else
 +
    raise new Exception(
 +
    'Попытка присвоить гурппе отрицательный номер!');
 +
end;
 +
 +
procedure Student.IncAge;
 +
begin
 +
  if fAge < MAX_AGE then
 +
    fAge += 1
 +
  else
 +
    raise new Exception(
 +
    'Выход за границы диапазона допустимого возраста [' +
 +
    MIN_AGE.ToString + '..' + MAX_AGE.ToString + ']!');
 +
end;
 +
 +
constructor Student.Create(Name: string; Age, Course, Group: integer);
 +
begin
 +
  SetName(Name);
 +
  SetAge(Age);
 +
  SetCourse(Course);
 +
  SetGroup(Group);
 +
end;
 +
 +
procedure Student.NextCourse;
 +
begin
 +
  if fCourse < MAX_COURSE then
 +
  begin
 +
    fCourse += 1;
 +
    IncAge;
 +
  end
 +
  else
 +
    raise new Exception(
 +
    'Выход за границы диапазона допустимых курсов [' +
 +
    MIN_COURSE.ToString + '..' + MAX_COURSE.ToString + ']!');
 +
end;
 +
 +
procedure Student.Print;
 +
begin
 +
  WriteFormat(
 +
  'Имя: {0}  Возраст: {1}  Курс: {2}  Группа: {3}  ',
 +
  fName, fAge, fCourse, fGroup);
 +
end;
 +
 +
procedure Student.Println;
 +
begin
 +
  Print;
 +
  writeln();
 +
end;
 +
 +
// ============================================ Teacher ===========================================
 +
 +
procedure Teacher.SetName(Name: string);
 +
begin
 +
  if Name <> '' then
 +
    fName := Name
 +
  else
 +
    raise new Exception(
 +
    'Попытка присвоить преподавателю пустое имя!');
 +
end;
 +
 +
constructor Teacher.Create(Name: string);
 +
begin
 +
  SetName(Name);
 +
end;
 +
 +
function Teacher.SayCourseWorkTheme(MyStudent: SeniorStudent): string;
 +
begin
 +
  result := 'Тема курсовой работы';
 +
end;
 +
 +
procedure Teacher.Print;
 +
begin
 +
  WriteFormat(
 +
  'Имя: {0}  ',
 +
  fName);
 +
end;
 +
 +
procedure Teacher.Println;
 +
begin
 +
  Print;
 +
  writeln();
 +
end;
 +
 +
// ========================================== EduModelType ======================================== 
 +
 +
function MaxCourseByEduModel(eduModel: EduModelType): integer;
 +
begin
 +
  case eduModel of
 +
    Bachelor: result := 4;
 +
    Specialist: result := 5;
 +
    Magister: result := 6;
 +
  end;
 +
end;
 +
 +
function StringEduModel(eduModel: EduModelType): string;
 +
begin
 +
  case eduModel of
 +
    Bachelor: result := 'Бакалавр';
 +
    Specialist: result := 'Специалист';
 +
    Magister: result := 'Магистр';
 +
  end;
 +
end;
 +
 
 +
 
 +
// ========================================= SeniorStudent ======================================== 
 +
 
 +
procedure SeniorStudent.SetAdvisor(Advisor: Teacher);
 +
begin
 +
  fAdvisor := Advisor;
 +
end;
 +
 +
procedure SeniorStudent.SetEduModel(EduModel: EduModelType);
 +
begin
 +
  fEduModel := EduModel;
 +
end;
 +
 +
procedure SeniorStudent.GetCourseWorkTheme;
 +
begin
 +
  fCourseWorkTheme := fAdvisor.SayCourseWorkTheme(self);
 +
end;
 +
 +
constructor SeniorStudent.Create(Name: string; Age, Course, Group: integer;
 +
  Advisor: Teacher; EduModel: EduModelType);
 +
begin
 +
  inherited Create(Name, Age, Course, Group);
 +
  fAdvisor := Advisor;
 +
  fEduModel := EduModel;
 +
  GetCourseWorkTheme;
 +
end;
 +
 +
procedure SeniorStudent.NextCourse;
 +
begin
 +
  var maxCourse := MaxCourseByEduModel(EduModel);
 +
  if fCourse < maxCourse then
 +
  begin
 +
    fCourse += 1;
 +
    IncAge;
 +
  end
 +
  else
 +
    raise new Exception(
 +
    'Выход за границы диапазона допустимых курсов [' +
 +
    MIN_COURSE.ToString + '..' + maxCourse.ToString + ']!');
 +
end;
 +
 +
procedure SeniorStudent.Print;
 +
begin
 +
  inherited Print;
 +
 
 +
  write('Преподаватель: '); 
 +
  fAdvisor.Print;
 +
  writeFormat('Тема курсовой работы: «{0}»  ', CourseWorkTheme);
 +
 
 +
  writeFormat('Образовательная степень: {0}', StringEduModel(EduModel));
 +
end;
 +
 +
procedure SeniorStudent.Println;
 +
begin
 +
  Print;
 +
  writeln();
 +
end;
 +
 +
end.
 +
</source>}}

Версия 13:02, 2 мая 2009

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

Введение

Иерархическая классификация животных в биологии

Наследование в программировании возникло как ответ на реальные отношения наследования классов в реальном мире и прикладных задачах.

————————————
Ввиду увеличения сложности задач в программировании, акцент переместился от алгоритмов к объектам, содержащим алгоритмы в качестве методов.
При этом, понятие главного алгоритма также потеряло свою важность. В больших проектах нет главного алгоритма или он тривиален, но есть большое число взаимосвязанных задач.

Пример. Главный алгоритм работы операционной системы.

* начальная инициализация
* цикл обработки сообщений
    если сообщение пришло то 
      обработать сообщение 
    до сообщения «Конец»
* заключительные действия

Как видим, этот алгоритм тривиален и ничего не говорит о том, как работает ОС.
————————————

Поскольку главный алгоритм сложной системы не существует или тривиален, то для программирования работы этой сложной системы мы:

  • выявляем классы объектов, присутствующих в этой системе
  • их свойства и методы
  • выявляем то общее, что есть в различных классах
  • выявляем различные взаимозависимости между классами и взаимодействие между объектами этих классов

Примеры взаимозависимостей и взаимодействия.

  1. класс содержит в качестве поля объект другого класса
  2. в методе класса параметром является объект другого класса
  3. метод класса вызывает статический метод другого класса

и т.д.

Замечание.
Взаимодействие классов устанавливается на этапе написания текста программы,
а взаимодействие конкретных объектов устанавливается на этапе выполнения программы

К отношениям между классами относится также то, при котором
один из классов является разновидностью другого.
Пример.

Пример наследования

Эти классы (StudentSeniorStudent) называют:

  • базовыйпроизводный
  • предокпотомок
  • надклассподкласс

Все производные классы наследуют от базового:

  • поля
  • методы
  • свойства

а также могут добавлять новые:

  • поля
  • свойства
  • методы

и переопределять (замещать)

  • некоторые методы базового класса


Каковы цели наследования?

  1. повторное использование кода
  2. обеспечение вариабельности и изменчивости кода

Наследование — это расширение или сужение?
Наследование — это расширение интерфейса класса, но сужение количества объектов (представителей) класса.

Наследование на примере Student - SeniorStudent

Про переменную Self см. здесь.

<xh4> Базовый класс Student </xh4>

interface
uses System;
   
const
  /// Минимальный допустимый возраст студента
  MIN_AGE = 1;
  /// Максимальный допустимый возраст студента
  MAX_AGE = 120;
  
  /// Минимальный возможный курс
  MIN_COURSE = 1;
  /// Максимальный возможный курс
  MAX_COURSE = 4;

type
  /// Студент
  Student = class
  private
    fName: string;
    fAge, fCourse, fGroup: integer;
    
    procedure SetName(Name: string);
    procedure SetAge(Age: integer);
    procedure SetCourse(Course: integer);
    procedure SetGroup(Group: integer);
    
    procedure IncAge;
    
  public
    /// Имя — только на чтение
    property Name: string read fName;
    /// Возраст — только на чтение
    property Age: integer read fAge;
    /// Курс — только на чтение
    property Course: integer read fCourse;
    /// Группа — только на чтение
    property Group: integer read fGroup;
    
    constructor Create(Name: string; Age, Course, Group: integer);
    
    procedure NextCourse;
    
    procedure Print;
    procedure Println;
  end;
  
implementation

procedure Student.SetName(Name: string);
begin
  if Name <> '' then
    fName := Name
  else
    raise new Exception(
    'Попытка присвоить студенту пустое имя!');
end;

procedure Student.SetAge(Age: integer);
begin
  if (Age >= MIN_AGE) and (Age <= MAX_AGE) then
    fAge := Age
  else
    raise new Exception(
    'Выход за границы диапазона допустимого возраста [' + 
    MIN_AGE.ToString + '..' + MAX_AGE.ToString + ']!');
end;

procedure Student.SetCourse(Course: integer);
begin
  if (Course >= MIN_COURSE) and (Course <= MAX_COURSE) then
    fCourse := Course
  else
    raise new Exception(
    'Выход за границы диапазона допустимых курсов [' + 
    MIN_COURSE.ToString + '..' + MAX_COURSE.ToString + ']!');
end;

procedure Student.SetGroup(Group: integer);
begin
  if (Group > 0) then
    fGroup := Group
  else
    raise new Exception(
    'Попытка присвоить гурппе отрицательный номер!');
end;

procedure Student.IncAge;
begin
  if fAge < MAX_AGE then
    fAge += 1
  else
    raise new Exception(
    'Выход за границы диапазона допустимого возраста [' + 
    MIN_AGE.ToString + '..' + MAX_AGE.ToString + ']!');
end;

constructor Student.Create(Name: string; Age, Course, Group: integer);
begin
  SetName(Name);
  SetAge(Age);
  SetCourse(Course);
  SetGroup(Group);
end;

procedure Student.NextCourse;
begin
  if fCourse < MAX_COURSE then
  begin
    fCourse += 1;
    IncAge;
  end
  else
    raise new Exception(
    'Выход за границы диапазона допустимых курсов [' + 
    MIN_COURSE.ToString + '..' + MAX_COURSE.ToString + ']!');
end;

procedure Student.Print;
begin
  WriteFormat(
  'Имя: {0}  Возраст: {1}  Курс: {2}  Группа: {3}',
  fName, fAge, fCourse, fGroup);
end;

procedure Student.Println;
begin
  Print;
  writeln();
end;

<xh4> Производный класс SeniorStudent </xh4> SeniorStudent — студент старших курсов.
Помимо имени, возраста, курса и группы:

  • у него будет научный руководитель (Advisor)
  • он будет знать тему курсовой работы, которую получает от своего научного руководителя
  • и для него будет известно, какую он получает образовательную степень (бакалавра, специалиста или магистра)

Будем считать, что класс преподавателя у нас уже есть, и есть метод SayCourseWorkTheme(MyStudent: SeniorStudent): string.
Или сделаем пока заглушку:

Приступим к реализации SeniorStudent.
Для начала нам понадобится вспомогательный тип EduModelType (образовательная степень), для лучшего использования которого напишем две функции:
function MaxCourseByEduModel(eduModel: EduModelType): integer — возвращающую максимальный курс, соответствующий образовательной степени eduModel
и function StringEduModel(eduModel: EduModelType): string — возвращающую строковое представление образовательной степени:

type
  /// Образовательная степень (Бакалавр, Специалист, Магистр)
  EduModelType = (Bachelor, Specialist, Magister);
  
/// Возвращает максимальный курс, соответствующий образовательной степени eduModel
function MaxCourseByEduModel(eduModel: EduModelType): integer; 
  
/// Возвращает строковое представление образовательной степени
function StringEduModel(eduModel: EduModelType): string;

implementation

function MaxCourseByEduModel(eduModel: EduModelType): integer; 
begin
  case eduModel of
    Bachelor: result := 4;
    Specialist: result := 5;
    Magister: result := 6;
  end;
end;

function StringEduModel(eduModel: EduModelType): string;
begin
  case eduModel of
    Bachelor: result := 'Бакалавр';
    Specialist: result := 'Специалист';
    Magister: result := 'Магистр';
  end;
end;

Теперь напишем интерфейс нашего старшекурсника:

  /// Студент старших курсов
  SeniorStudent = class (Student)
  private
    fAdvisor: Teacher;
    fEduModel: EduModelType;
    fCourseWorkTheme: string;
    
    procedure SetAdvisor(Advisor: Teacher);
    procedure SetEduModel(EduModel: EduModelType);
    
    /// Устанавливает тему курсовой работы, получая её от научного руководителя
    procedure GetCourseWorkTheme;
    
  public
    /// Научный руководитель — только на чтение
    property Advisor: Teacher read fAdvisor;
    /// Образовательная степень — только на чтение
    property EduModel: EduModelType read fEduModel;
    /// Тема курсовой работы — только на чтение
    property CourseWorkTheme: string read fCourseWorkTheme;
    
    constructor Create(Name: string; Age, Course, Group: integer; 
      Advisor: Teacher; EduModel: EduModelType);
    
    procedure NextCourse;
    
    procedure Print;
    procedure Println;
  end;

Как реализовать сеттеры, GetCourseWorkTheme и NextCourse мы знаем:

procedure SeniorStudent.SetAdvisor(Advisor: Teacher);
begin
  fAdvisor := Advisor;
end;

procedure SeniorStudent.SetEduModel(EduModel: EduModelType);
begin
  fEduModel := EduModel;
end;

procedure SeniorStudent.GetCourseWorkTheme;
begin
  fCourseWorkTheme := fAdvisor.SayCourseWorkTheme(self);
end;

procedure SeniorStudent.NextCourse;
begin
  var maxCourse := MaxCourseByEduModel(EduModel);
  if fCourse < maxCourse then
  begin
    fCourse += 1;
    IncAge;
  end
  else
    raise new Exception(
    'Выход за границы диапазона допустимых курсов [' + 
    MIN_COURSE.ToString + '..' + maxCourse.ToString + ']!');
end;

Замечание. В процессе разработки возникла проблема доступа к приватным полям предка (fCourse)
Если Student и SeniorStudent реализованы в одном модуле, то такой проблемы нет и доступ возможен, однако, если они они реализованы в разных модулях, то данный код не откомпилируется.
Исправим это в дальнейшем, а пока будем считать, что классы реализованы в одном модуле.

Заметим, что процедура NextCourse была полностью переопределена в потомке.
Но, как правило, одноименный метод в потомке не переписывается полностью, а вызывает соответствующий метод предка.

Переопределяющий метод называется также заменяющим.

Для вызова в методе Method замещенного метода предка используется конструкция

inherited Method

Inherited — значит унаследованный.

Воспользуемся этим при реализации оставшихся методов:

constructor SeniorStudent.Create(Name: string; Age, Course, Group: integer; 
  Advisor: Teacher; EduModel: EduModelType);
begin
  inherited Create(Name, Age, Course, Group);
  fAdvisor := Advisor;
  fEduModel := EduModel;
  GetCourseWorkTheme;
end;

procedure SeniorStudent.Print;
begin
  inherited Print;
  
  write('Преподаватель: ');  
  fAdvisor.Print;
  writeFormat('Тема курсовой работы: «{0}»  ', CourseWorkTheme);
  
  writeFormat('Образовательная степень: {0}', StringEduModel(EduModel));
end;

procedure SeniorStudent.Println;
begin
  Print;
  writeln();
end;