- •Составление программ с использованиемобъектно-ориентированного подхода
- •Теоретические сведения
- •Инкапсуляция
- •Наследование
- •Полиморфизм
- •Виртуальные методы
- •Конструкторы
- •Присваивание объектов
- •Пример программирования с использованием объектных типов
- •Лабораторное задание
- •Порядок выполнения лабораторной работы
- •Требования к отчету
- •Контрольные вопросы
Наследование
При построении нового объектного типа можно использовать некоторый ранее определенный объектный тип. Пусть, например, необходимо построить объектный тип TCircle, управляющий геометрической фигурой - окружностью - на экране дисплея. При этом можно воспользоваться ранее определенным типом TPoints, т.к. для TCircle также необходимы поля X и Y для хранения координат центра окружности и логическое поле Visible для определения видимости окружности в текущий момент. Остается добавить поле для хранения величины радиуса окружности, а также методы для работы с окружностью.
Новый объектный тип TCircle может быть объявлен производным типом (потомком) ранее объявленного объектного типа TPoints, который в этом случае называется прародительским типом (прародителем). При объявлении типа-потомка имя прародителя указывается в круглых скобках после служебного слова Class (рис.6).
Type
TCircle =class(TPoints)
Radius:Word;
<новые методы>
End;
Рис.6. Описание объектного типа-потомка
В результате такого объявления объектный тип - потомок TCircle получил все поля и методы своего прародителя объектного типа TPoints, а также дополнил их своими полями и методами.
Такой прием, в результате которого новый объектный тип приобретает поля и методы другого объектного типа, называется наследованием.
Ряд наследования можно продолжить и расширить (рис.7). Каждый новый объектный тип-потомок может иметь только одного прародителя; в то же время он может сам стать прародителем для произвольного числа типов-потомков.
Так, например, можно определить объектный тип TCirFill (закрашенный круг), потомок объектного типа TCircle, добавив поле Color, определяющее цвет круга, и, возможно, новые методы (рис.7).
Можно определить объектный тип THoop (кольцо, состоящее из двух концентрических окружностей), добавив поле Radius2 (рис.7).
Длина такой цепочки наследования ничем не ограничивается. В результате наследования создается иерархическое дерево объектных типов (рис.8).
Рис.8. Иерархия объектных типов
Type
TCirFill = class (TCircle)
Color:Word;
<новые методы>
End;
THoop = class (TCircle)
Radius2:Word;
<новые методы>
End;
Рис.7. Образование новых типов путем наследования
Полиморфизм
Очевидно, что в объектном типе-потомке имена вновь объявляемых полей должны отличаться от имен полей, объявленных ранее в прародительских типах.
А как обстоит дело с именами новых методов? Могут ли они совпадать с именами методов полученных по наследству?
Вернемся к нашему примеру. По праву наследования объектный тип TCircle имеет в числе своих компонент все методы прародителя TPoints. Легко видеть,что:
- метод Init, задающий значения координат точки, является недостаточным для задания окружности: радиус остаётся неопределённым;
- методы GetX и GetY, возвращающие текущие значения координат центра окружности, вполне применимы в объектном типе TCircle;
- методы Show и Hide не подходят для рисования и стирания окружности;
- метод Move мог бы передвигать окружность по экрану дисплея, если бы содержащиеся в нём методы Show и Hide подходили для рисования и стирания окружности.
В связи с этим при объявлении объектного типа TCircle потомком объектного типа TPoints необходимо изменить содержание унаследованных методов Init, Show, Hide и Move, переориентировать их на работу с окружностью, чтобы они выполняли с окружностью действия, аналогичные тем, которые выполняются с точкой. С учетом этих замечаний объявление объектного типа TCircle будет таким, как показано на рис.9.
+--------------------------------------------------------------+
¦ Type ¦
¦ TCircle = Class(TPoints) ¦
¦ Radius:Word; { Радиус окружности }¦
¦ Function GetR:Word; { Вернуть текущее }¦
¦ { значение радиуса }¦
¦ { окружности }¦
¦ Procedure Init(A,B:Integer; R:Word); ¦
¦ Procedure Show; { Нарисовать окружность }¦
¦ { текущим цветом пера }¦
¦ Procedure Hide; { Стереть окружность }¦
¦ Procedure Move(DX,DY:Integer) ¦
¦ { Переместить окружность}¦
¦ { в новое положение, }¦
¦ { определяемое вектором }¦
¦ { (DX,DY) }¦
¦ End; ¦
¦ Procedure TCircle.Init(A,B:Integer; R:Word); ¦
¦ Begin ¦
¦ TPoints.Init(A,B); ¦
¦ Radius:=R ¦
¦ End; ¦
¦ Function TCircle.GetR:Word; ¦
¦ Begin ¦
¦ GetR:=Radius; ¦
¦ End; ¦
¦ Procedure TCircle.Show; ¦
¦ Begin ¦
¦ Form1.Canvas.Arc(X-Radius,Y-Radius,X+Radius,Y+Radius, ¦
¦ X,0,X,0); ¦
¦ Visible:=True ¦
¦ End; ¦
¦ Procedure TCircle.Hide; ¦
¦ Var ¦
¦ Color:TColor; ¦
¦ Begin ¦
¦ Color:= Form1.Canvas.Pen.Color;{ Запомнить цвет пера } ¦
¦ Form1.Canvas.Pen.Color :={ Установить цвет пера такой } ¦
¦ Form1.Color { же,как цвет фона } ¦
¦ Show; { Нарисовать окружность текущим ¦
¦ цветом пера, равным цвету фона. ¦
¦ Окружность будет стёрта } ¦
¦ Visible:=False; ¦
¦ Form1.Canvas.Pen.Color:=Color { Восстановить цвет пера} ¦
¦ End; ¦
¦ Procedure TCircle.Move(DX,DY:Integer); ¦
¦ Var ¦
¦ B:Boolean; ¦
¦ Begin ¦
¦ B:=Visible; ¦
¦ If B Then Hide; { Если окружность видима, то стереть}¦
¦ X:=X+DX; { Изменить координаты центра }¦
¦ Y:=Y+DY; ¦
¦ If B Then Show; { Если окружность была видимой,то }¦
¦ { нарисовать её в новом положении }¦
¦ End; ¦
+--------------------------------------------------------------+
Рис.9
В результате такого объявления объектный тип TCircle содержит следующие компоненты (рис.10):
- поля X, Y, Visible, унаследованные от прародителя TPoints;
- собственное поле Radius;
- метод TCircle.Init, переопределяющий полученный по наследству метод TPoints.Init. При этом для инициализации полей X, Y, Visible используется вызов метода TPoints.Init, который доступен объектному типу TCircle. Это показано на схеме стрелкой, идущей от TCircle.Init к TPoints.Init;
- два унаследованных метода GetX, GetY;
- новый метод GetR для доступа к текущему значению радиуса окружности;
- методы TCircle.Show и TCircle.Hide, переопределяющие методы TPoints.Show и TPoints.Hide, что имеет очевидный смысл: для рисования и стирания окружности требуются иные алгоритмы, нежели для рисования и стирания точки. Причем в методе Hide содержится вызов метода Show, и это показано на схеме стрелкой;
- метод TCircle.Move, также переопределяющий полученный по наследству метод TPoints.Move. Причем в методе TСircle.Move содержится вызов методов Show и Hide, и это показано стрелками на схеме. Связи методов, показанные стрелками, будут жестко установлены на этапе компиляции.
Таким образом, в прародителе TPoints (см. рис.1, 2) и потомке TCircle (рис.9) объявлены одноимённые методы Init, Show, Hide, Move разного содержания.
Возможность называть одним именем разные методы, относящиеся к разным родственным объектным типам, носит название - полиморфизм.
+---------------------------------------------------------------+
¦ TCircle ¦
¦ +----------------------------+ ¦
¦ ¦ TPoints ¦ ¦
¦ ¦ +---------------+ ¦ ¦
¦ ¦ ¦ X ¦ ¦ ¦
¦ ¦ +---------------¦ ¦ +---------------+ ¦
¦ ¦ ¦ Y ¦ ¦ ¦ Radius ¦ ¦
¦ ¦ +---------------¦ ¦ +---------------¦ ¦
¦ ¦ ¦ Visible ¦ ¦ ¦ GetR ¦ ¦
¦ ¦ +---------------¦ ¦ +---------------¦ ¦
¦ ¦ ¦ Init +<-------+------¦ Init ¦ ¦
¦ ¦ +---------------¦ ¦ +---------------¦ ¦
¦ ¦ ¦ GetX ¦ ¦ +-->¦ Show +<-----+ ¦
¦ ¦ +---------------¦ ¦ ¦ +---------------¦ ¦ ¦
¦ ¦ ¦ GetY ¦ ¦ +---¦ Hide +<--+ ¦ ¦
¦ ¦ +---------------¦ ¦ +---------------¦ ¦ ¦ ¦
¦ ¦ ¦ Show +<-----+ ¦ ¦ Move +------+ ¦
¦ ¦ +---------------¦ ¦ ¦ +---------------+ ¦
¦ ¦ ¦ Hide +<--+ ¦ ¦ ¦
¦ ¦ +---------------¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ Move +------+ ¦ ¦
¦ ¦ +---------------+ ¦ ¦
¦ +----------------------------+ ¦
+---------------------------------------------------------------+
Рис.10
При построении иерархического дерева объектных типов нужно иметь в виду следующее: если метод, полученный в наследство от прародителя, переопределяется потомком, то ко всем последующим потомкам переходит переопределённый метод (пока он не будет снова переопределён). Однако в прародителях действуют те методы, которые были определены именно для них.
Так, в объектном типе TPoints действуют методы TPoints.Init, TPoints.Show, TPoints.Hide и TPoints.Move, несмотря на то, что в объектном типе TCircle эти методы были переопределены.
Для примера рассмотрим фрагмент программы на рис.11, в котором сначала по экрану движется точка, затем по экрану движется окружность.
В результате методы Init, Show, Hide, Move над экземпляром Р прародителя TPoints выполняют одни действия, а над экземпляром С потомка TCircle - другие. Реализация этих методов зависит от того, над экземплярами каких объектных типов они будут выполняться. В этом заключается проявление полиморфизма.
+---------------------------------------------------------------+
¦ Type ¦
¦ TPoints = Class ¦
¦ X,Y :Integer; ¦
¦ Visible:Boolean; ¦
¦ Procedure Init(A,B:Integer); ¦
¦ Function GetX:Integer; ¦
¦ Function GetY:Integer; ¦
¦ Procedure Show; ¦
¦ Procedure Hide; ¦
¦ Procedure Move(DX,DY:Integer) ¦
¦ End; ¦
¦ TCircle = Class(TPoints) ¦
¦ Radius:Word; ¦
¦ Procedure Init(A,B:Integer; R:Word); ¦
¦ Function GetR:Word; ¦
¦ Procedure Show; ¦
¦ Procedure Hide; ¦
¦ Procedure Move(DX,DY:Integer) ¦
¦ End; ¦
¦ { ....... Описание методов (Рис.2 и 9) ....... } ¦
¦ Var ¦
¦ P:TPoints; ¦
¦ C:TCircle; ¦
¦ ... ¦
¦ BEGIN ¦
¦ P:=TPoints.Create; {Создать экземпляр точки}¦
¦ C:=TCircle.Create; {Создать экземпляр окружности}¦
¦ ... ¦
¦ P.Init(50,50); ¦
¦ With P Do ¦
¦ Begin ¦
¦ Show; { Зажечь точку }¦
¦ While GetY<=150 Do { Передвигать точку по экрану }¦
¦ Move(10,10); ¦
¦ { вправо вниз }¦
¦ Hide { Погасить точку }¦
¦ End; ¦
¦ C.Init(50,50,50); ¦
¦ With C Do ¦
¦ Begin ¦
¦ Show; { Нарисовать окружность }¦
¦ While GetY<=150 Do { Передвигать окружность по }¦
¦ Move(10,10); { экрану вправо вниз }¦
¦ Hide { Стереть окружность }¦
¦ End; ¦
¦ ... ¦
¦ P.Destroy; {Уничтожить экземпляр точки}¦
¦ C.Destroy; {Уничтожить экземпляр окружности}¦
¦ ... ¦
+---------------------------------------------------------------+
Рис.11