Основы программирования — второй семестр 08-09; Михалкович С.С.; VI часть — различия между версиями
Juliet (обсуждение | вклад) (→Наследование на примере Student - SeniorStudent) |
Juliet (обсуждение | вклад) (→Наследование на примере 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
Наследование
Введение
Наследование в программировании возникло как ответ на реальные отношения наследования классов в реальном мире и прикладных задачах.
————————————
Ввиду увеличения сложности задач в программировании, акцент переместился от алгоритмов к объектам, содержащим алгоритмы в качестве методов.
При этом, понятие главного алгоритма также потеряло свою важность. В больших проектах нет главного алгоритма или он тривиален, но есть большое число взаимосвязанных задач.
Пример. Главный алгоритм работы операционной системы.
* начальная инициализация * цикл обработки сообщений если сообщение пришло то обработать сообщение до сообщения «Конец» * заключительные действия
Как видим, этот алгоритм тривиален и ничего не говорит о том, как работает ОС.
————————————
Поскольку главный алгоритм сложной системы не существует или тривиален, то для программирования работы этой сложной системы мы:
- выявляем классы объектов, присутствующих в этой системе
- их свойства и методы
- выявляем то общее, что есть в различных классах
- выявляем различные взаимозависимости между классами и взаимодействие между объектами этих классов
Примеры взаимозависимостей и взаимодействия.
- класс содержит в качестве поля объект другого класса
- в методе класса параметром является объект другого класса
- метод класса вызывает статический метод другого класса
и т.д.
Замечание.
Взаимодействие классов устанавливается на этапе написания текста программы,
а взаимодействие конкретных объектов устанавливается на этапе выполнения программы
К отношениям между классами относится также то, при котором
один из классов является разновидностью другого.
Пример.
Эти классы (Student — SeniorStudent) называют:
- базовый — производный
- предок — потомок
- надкласс — подкласс
Все производные классы наследуют от базового:
- поля
- методы
- свойства
а также могут добавлять новые:
- поля
- свойства
- методы
и переопределять (замещать)
- некоторые методы базового класса
Каковы цели наследования?
- повторное использование кода
- обеспечение вариабельности и изменчивости кода
Наследование — это расширение или сужение?
Наследование — это расширение интерфейса класса, но сужение количества объектов (представителей) класса.
Наследование на примере 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.
Или сделаем пока заглушку:
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;
Приступим к реализации 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;
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.