Информатика в техническом университете / Информатика в техническом университете. Объектно ориентированное программирование
.pdf5. Объектная модель Delphi Pascal
Procedure Draw; |
{рисование} |
|
Procedure ReSize(ar: Word); {изменение размеров} |
||
Procedure ReColor(acolor: TColor); {изменение цвета} |
||
end; |
|
|
Implementation |
|
|
Constructor TMyCircle,Create; |
|
|
Begin inherited Create; |
{вызвать наследуемый конструктор} |
|
Image: =almage; |
{инициализировать поле} |
|
x:=ax; у:-ay; |
r:=ar; |
Color: =aColor; |
End;
Procedure TMyCircle.Draw;
Begin
Image.Canvas.Pen.Color: =Color; {задать цвет пера}
Image.Canvas.Ellipse(x-r, y-r, x+r, y+r); {нарисовать окружность}
End;
Procedure TMyCircle. Clear;
Var TempColor: TColor;
Begin
TempColor: =Color; |
{сохранить цвет пера} |
||
Color: =Image. Canvas.Brush. Color; {фиксировать цвет фона} |
|||
Draw; |
|
{нарисовать цветом фона - стереть } |
|
Color:=TempColor; |
{востановить цвет пера} |
||
End; |
|
|
|
Procedure TMyCircle.ReSize; |
|
||
Begin |
Clear; |
r:=ar; |
Draw; End; |
Procedure TMyCircul.ReColor(aColor: TColor); |
|||
Begin |
Clear; |
Color: =aColor; Draw; End; |
|
End |
|
|
|
Теперь переходим к программированию обработчиков событий, при наступлении которых должны выполняться основные операции приложения. Ниже приводится текст модуля Main, соответствующего форме TMainForm.
Unit Main;
Interface
Uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, ComCtrls;
Type
TMainForm = class(TForm)
Bevel: TBevel; |
{рамка} |
Image: TImage; |
{поле рисования} |
ColorButton, ExitButton: TButton; {кнопки}
rEdit: TEdit; |
{редактор ввода радиуса} |
rLabel: TLabel; |
{метка ввода радиуса} |
200
5.7. Определение класса
UpDown: TUpDown; {компонент «инкремент/декремент»}
ColorDialog: TColorDialog; {диалог выбора цвета}
Procedure FormActivate{Sender: TObject);
Procedure ImageMouseDown{Sender: TObject; Button: TMouseButton; Shift: TShiftState; AT, Y: Integer);
Procedure UpDownClick{Sender: TObject; Button: TUDBtnType); Procedure ColorButtonClick(Sender: TObject);
Procedure ExitButtonClick(Sender: TObject); end;
Var MainForm: TMainForm; Implementation
{$R ''.DFM} Uses Circul; VarC'.TMyCircle;
Procedure TMainForm. FormActivate{Sender: TObject);
Begin Image1. Canvas.Brush. Color: =clWhite; {установить белый фон} ImageI. Canvas.Pen. Color: =clBlack; {установить цвет рисования}
End;
Procedure TMainForm.ImageMouseDown{Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
Begin |
|
|
|
|
if Button=mbLeft then |
{если нажата левая клавиша мыши} |
|||
begin |
|
|
|
|
С.Free; |
|
{если объект создан, то уничтожить его} |
||
С: -TMyCircle.Create(Imagel |
,Х, Y,strtoint(rEdit. Text), |
|||
С.Draw; |
Image1.Canvas.Pen.Color); |
{конструировать} |
||
{изобразить объект с заданными параметрами} |
||||
end; |
|
|
|
|
End; |
|
|
|
|
Procedure TMainForm. UpDownl Click(Sender: TObject; |
||||
Begin |
|
|
Button: TUDBtnType); |
|
|
|
|
|
|
ifConil then |
|
|
{если объект создан, то} |
|
C.ReSize(strtoint(rEdit. Text)); |
{перерисовать с другим радиусом} |
|||
End; |
|
|
|
|
Procedure TMainForm.ColorButtonClick(Sender: |
TObject); |
|||
Begin |
|
|
|
|
ifColorDialog.Execute |
then {если вьшолнен диалог выбора цвета, то} |
|||
ImageI.Canvas.Pen.Color: =ColorDialog. Color; {установить цвет} |
||||
ifC< >nil then |
|
|
{если объект создан, то} |
|
C.ReColor(Imagel.Canvas.Pen.Color); {перерисовать другим цветом} |
||||
End; |
|
|
|
|
Procedure TMainForm.ExitButtonClick(Sender: |
TObject); |
201
5. Объектная модель Delphi Pascal
Begin Close; End; Initialization
Finalization C.Free; {если объект создан, то уничтожить его}
End.
Из приведенного текста видно, что конкретный объект класса TMyCircle создается при нажатии левой клавиши мыши. Причем, если объект уже существовал, то он уничтожается. Соответствующая уничтоженному объекту окружность на холсте сохраняется, но теряется связь изображения и объекта.
После этого мы больше не можем менять цвет и размер этой окружности. Таким образом, одна и та же переменная используется многократно для хранения адресов различньпс объектов при рисовании. Последний используемый объект уничтожается при закрытии формы, когда вьшолняются операщш, записанные
всекщш finalization.
5.2.Особенности реализации переопределения методов
ВDelphi Pascal реализованы более сложные механизмы переопределения методов. Прежде всего в нем различают: виртуальные методы, динамические методы и абстрактные методы. Кроме того, в последних версиях языка реализована параметрическая перегрузка как обычных процедур и функщш, так и методов. Использование соответствующих средств позволяет создавать существенно более мопщые классы.
Виртуальные методы. Виртуальные методы объектной модели, используемой Delphi, практически ничем не отличаются от тех, которые определялись в Borland Pascal 7.0. Для них также реализуется механизм позднего связывания, обеспечивая возможность построения виртуальных объектов. Не изменилась и сущность реализации механизма позднего связывания через ТВМ. Изменения коснулись лишь описания виртуальных методов. Теперь только самый первый виртуальный метод в иерархии описьшается virtual^ все же методы, перекрывающие этот метод, описьшаются со спещ1фикатором override. Если же по ошибке какой-нибудь из этих методов описать virtual, то будет создано новое семейство виртуальных полиморфных методов, родоначальником которого является метод, описанный virtual по ошибке.
Примечание. Вызов конструктора выполняется следующим образом. Вначале он вызывается как метод класса и организует размещение в памяти нового объекта, а затем вьпывается уже как обычный метод. Поэтому конструктор может объявляться виртуальным. Виртуальный конструктор позволяет создавать объекты различных типов, устанавливаемых во время выполнения программы.
Динамические методы. Динамическими в Delphi называются полиморфные виртуальные методы, доступ к которым вьшолняется не через
202
5.2. Особенности реализации переопределения методов
ТВМ, а через специальную таблицу динамических методов (ТДМ). Такие методы описываются, соответственно, dymamic - при объявлении и override - при переопределении.
В отличие от ТВМ, которая хранит адреса всех виртуальных методов данного класса, ТДМ хранит только адреса виртуальных методов, определенных в данном классе (рис. 5.5). Если осуществляется вызов динамического метода, определе1шого в предках данного класса, то его адрес отыскивается в организованных в списковую структуру описаниях классов иерархии (ТВМ - раздел 5.6). Естественно, поиск такого метода займет больше времени по сравнению с виртуальными методами. Однако ТДМ занимает меньше места, так как не хранит всех предшествующих ссьшок.
Из вьппесказанного следует, что использовать динамическую реализацию полиморфных виртуальных методов имеет смысл лишь в тех случаях, когда класс имеет сотни потомков, а описываемый метод вызьюается крайне редко, или если метод переопределяется в каждом потомке. Во всех остальных случаях лучше использовать обычную реализацию (virtual).
Абстрактные методы. Абстрактные методы используются при объявлении методов, реализация которьгх откладывается. Такие методы в классе описываются служебным словом abstract и обязательно переопределяются в потомках класса.
Класс, в состав которого входят методы с отложенной реализацией, назьшается абстрактным. Создавать (конструрфовать) объекты абстрактных
классов запрещается. |
|
|
|
|
Методы: |
Класс A |
|
|
TBM: |
||
Класс А |
VI -virtual |
||
Метод VI |
|||
V2 - virtual |
|||
|
Метод V2 |
||
|
Dl -dynamic |
ТДМ: |
|
|
D2 - dynamic |
||
|
Метод Dl |
||
|
|
||
Класс В |
Методы: |
Метод D2 |
|
VI -ovemde |
|
||
|
Класс Б |
||
|
Dl -oven-ide |
||
|
|
Родитель- |
|
|
|
ТВМ: |
|
|
|
Метод VI |
|
|
|
Метод V2 |
|
|
|
ТДМ: |
|
|
|
Метод D1 |
Рис. 5.5. Виртуальные и динамические методы
203
5. Объектная модель Delphi Pascal
Создать и нарисовать '^ |
^ |
Создать и нарисовать 1 |
|||
< Изменить размер |
Окно |
|
|
Изменить размер |
|
Окружность |
приложения |
|
Изменить цвет |
Квадрат |
|
Изменить цвет |
|
|
|
|
|
Уничтожить |
|
|
i 1 |
Уничтожить |
Ы |
|
|
|
— |
||
Рис. 5.6. Объектная декомпозиция приножения «Окружности и квадраты» |
|||||
Пример 5.2. Использование абстрактных методов (графический |
|||||
редактор «Окружности и квадраты» - |
вариант 1). Попробуем изменить |
пример предыдущего раздела так, чтобы можно было рисовать не только окружности, но и, например, квадраты.
Диаграмма объектов такого приложения изображена на рис. 5.6. Естественно, теперь интерфейс должен обеспечивать возможность
выбора фигуры. С этой целью добавим в форму компонент TRadioGroup группу «радиокнопок» (рис. 5.7).
Соответственно при обработке события С2 (см. рис.5.3) необходимо учитывать тип выбранной фигуры.
Класс TMainForm будет иметь ту же структуру, что и в предыдущем примере, но в него будет добавлено поле RadioGroup типа TRadioButton.
Класс TMySquare можно построить несколькими способами.
Графический редактор "Окружности и квадраты"
Радиус круга:
13 g
Цвет
рВид фигуры:—!
О Окружность] 'TRadioGroup
® |квадрат|
I Выход I
Рис. 5.7. Вид главного окна приложения «Окружности и квадраты»
204
5.2.Особенности реализации переопределения методов
1.Класс TMySquare можно наследовать от TObject, так же как был получен классТМуС1гс1е. В этом случае нам придется повторить программирование всех методов данного класса, что явно нецелесообразно.
2.Класс TMySquare можно наследовать от TMyCircle (рис.5.8, а). Тогда мы можем наследовать от TMyCircle методы Create, Clear, ReColor, ReSize, определяющие общие элементы поведения. Метод Draw необходимо объявить виртуальным, так как он вызьшается из наследуемых методов и переопределен
вклассе-потомке. Метод Draw класса TMySquare должен быть объявлен переопределенным - override. Данный вариант наследования, принщшиально применимый в конкретном случае, является не универсальным (и, в общем, не логичным: как можно «наследовать» от круга квадрат?).
3.С учетом существования двух типов объектов со сходным поведением можно создать абстрактный класс TFigure, инкапсулирующий требуемый тип поведения фигур (рис. 5.8, б), В этом классе нужно объявить метод Draw виртуальным (virtual) и абстрактным (abstract) и определить методы Create, Clear, ReColor, ReSize через метод Draw. Теперь мы можем наследовать от этого абстрактного класса классы, рисующие любые фигуры. Эти классы должны будут переопределять абстрактный метод Draw класса TMyFigure.
Представленный ниже текст модуля Figxire включает описание иерархии классов в соответствии с рис. 5.8, б.
Unit Figure; |
|
|
Interface |
|
|
Uses extctrIs,Graphics; |
|
|
Type TMyFigure=class |
|
|
private |
x,y,r:Word; Color: TColor; |
Image:TImage; |
|
procedure Clear; |
|
public
Construetor Create(almage: TImage;ax, ay, ar: Word;aColor: TColor); Procedure Draw; virtual; abstract; {абстрактная процедура}
Простая иерархия |
Иерархия с абстрактным классом |
leiil
TMyCircle
Поля:
Image, х у, г, Color Методы:
Create, ReColor,
Clear, ReSize, Draw; virtual
tlliBi
TMyFigure
Поля:
Image, x y, r, Color Методы:
Create, ReColor,
Clear, ReSize,
Draw; virtual; abstract
TMySquare |
TMyCircle |
TMySquare |
|
Метод: Draw, override Метод: Draw, override |
|||
Метод: Draw, override |
|||
|
б |
||
a |
|
Рис. 5.8. Два варианта иерархии классов
205
|
5. Объектная модель Delphi Pascal |
||
Procedure ReSizefar: Word); |
|
|
|
Procedure ReColor(acolor:TColor); |
|
|
|
end; |
|
|
|
TMyCircle=class(TMyFigure) |
{класс Окружность} |
||
public |
Procedure Draw; override; |
{рисование окружности} |
|
end; |
|
|
|
TMySquare=class(TMyFigure) |
{класс Квадрат} |
||
public |
Procedure Draw; override; |
{рисование квадрата} |
|
end; |
|
|
|
Implementation |
|
|
|
Constructor TMyFigure.Create; |
|
|
|
Begin |
|
|
|
inherited Create; |
|
|
|
Image: =almage; x:=ax; y:=ay; |
r:=ar; Color: =aColor; |
||
End; |
|
|
|
Procedure TMyFigure.Clear; |
|
|
Var TempColor:TColor; Begin TempColor: =Color;
Color: =Image. Canvas.Brush. Color;
Draw; {нарисовать фигуру цветом фона - стереть}
Color: =TempColor; |
|
||
End; |
|
|
|
Procedure TMyFigure.Resize; |
|
||
Begin |
Clear; |
r:=ar; Draw; |
End; |
Procedure TMyFigure.Recolor; |
Draw; End; |
||
Begin |
Clear; |
Color: =aColor; |
|
Procedure TMyCircle.Draw; |
|
||
Begin |
Image. Canvas.Pen. Color:=Color; |
||
|
Image. Canvas.Ellipse(x-r,y'r^'^r,y-^r); |
End;
Procedure TMySquare.Draw;
Begin Image. Canvas.Pen. Color:=Color;
Image. Canvas.Rectangle(x-r,y-r^+r,y+r);
End;
End.
Пример демонстрирует, что использование абстрактных классов позволяет разрабатывать более универсальные и логически обоснованные иерархии.
Пр именами е. При создании аналогичных иерархий средствами Borland Pascal 7.0 приходилось использовать «пустые» методы, включающие только операторные скобки begin end. При этом компилятор не мог контролировать создание объектов абстрактных классов, предоставляя разработчику самостоятельно отыскивать ошибки данного ввда.
206
5.2. Особенности реализации переопределения методов
Перегрузка методов. Начиная с версии 4, в Delphi Pascal появилась возможность перегрузки процедур и функций:
function |
DividefX, Y: Real): Real; overload; {вариант 1} |
begin |
Result: = XJY; end; |
function |
Divide(X, Y: Integer): Integer; overload; {вариант 2} |
begin |
Result: = Xdiv Y; end; |
Какой конкретно вариант перегруженных процедур и функций вызван, определяется по типам фактических параметров:
k:=Divide(2.5, 7.3); {вызывается вариант 1} к: =Divide(6,3); {вызывается вариант 2}
Перегружать можно не только простые процедуры и функции, но и методы. Перегруэюенныи метод не перекрывается как переопределенный, а
остается доступным, как любой другой метод базового класса.
Для описания перегруженных методов используется служебное слово overload. Нужный аспект метода при его перегрузке также определяется по совпадению типов фактических параметров:
type
Т1 = class(TObject) public
procedure Test(I: Integer); overload; end;
T2 = class(Tl) public
procedure Test(S: string); overload; end;
Var SomeObject: T2; |
|
SomeObject := T2.Create; |
|
SomeObject.TestCHello!'); |
{параметр строка - вызвается метод Test класса |
SomeObject. Test(7); |
Т2} |
{параметр целое число - вызывается метод Test |
|
|
класса Т1} |
В отличие от перегруженных методов C++ перегруженные методы Delphi Pascal не могут объявляться внутри одного класса:
Туре
TSomeClass = class
207
5. Объектная модель Delphi Pascal
public
function Func(P: Integer): Integer;
function Func(P: Boolean): Integer { ошибка!}
Если перегружается виртуальный метод, то компилятор выдает предупреждение о перекрытии доступа к этому методу. Для того чтобы сообщить компилятору, что некоторый метод действительно должен перекрыть витруальный метод, используется служебное слово reintroduce.
Рассмотрим различные варианты переопределения виртуальных методов:
Туре
Т1 = class(TObject)
public procedure ActI; virtual; procedure Act2; virtual; procedure Act3; virtual; procedure Act4; virtual; procedure Act5; virtual;
end;
T2 = class(Tl) public
procedure Act 1;override; {определение нового аспекта виртуального полиморфного метода}
procedure Act2;virtual; {определяется новый полиморфный метод - компилятор выдает предупреждение о возможной ошибке, так как при этом доступ к ранее описанному полиморфному методу перекрывается}
procedure Act3;reintroduce;virtual; {определяется новый полиморфный метод - компилятор предупреждения не выдает, так как пресечение доступа документировано}
procedure Act4; {переопределение виртуального метода простым - компилятор выдает предупреждение о возможной ошибке, так как при этом доступ к виртуальному мето,цу перекрывается}
procedure Act5; reintroduce; {переопределение виртуального метода простым - компилятор предупреждения не выдает, так как пресечение доступа документировано}
end;
var SomeObjectl: Tl; begin
SomeObjectl ;= T2.Create; {указатель на базовый класс содержит адрес объекта производного класса}
SomeObjectl Act 1; {позднее связывание - вызывается метод класса Т2} SomeObjectl.Act2; {раннее связывание - вызывается метод класса Т1}
208
5.2. Особенности реализации переопределения методов
SomeObjectl.Act3; {раьшее связывание - вызывается метод класса Т1} SomeObjectl.Act4; {раннее связывание - вызывается метод класса Т1} SomeObjectl .Act5; {раннее связывание - вызывается метод класса Т1} end;
Соответственно, описание классов, использующих перегрузку виртуальных методов, должно вьшолняться по следующему образцу:
type
Т1 = class(TObject) public
procedure Test(I: Integer); overload; virtual; end;
T2 = class(Tl) public
procedure Test(S: string); reintroduce; overload; end;
При перегрузке как обычных процедур и функций, так и методов, необходимо очень осторожно обращаться с параметрами, принимаемыми по умолчанию (которые также введены начиная с версии 4 Delphi):
procedure Confused(I: Integer); overload; Begin... End;
procedure Confused (I: Integer; J: Integer = 0); overload; Begin... End;
Confused(X); {ошибка компиляции, не определен вариант перегруженной процедуры}
Аналогичная ситуация при перегрузке методов не приводит к появлению сообщения об ошибке, просто один из перегруженных методов становится недоступным (в примере ниже недоступен перегруженный метод базового класса):
Туре
Т1 = class(TObject) public
procedure Test(I: Integer); overload; virtual; end;
T2 = class(TI) public
procedureTest (I:Integer;S:string= 'aaa ) ; reintroduce; overload; end;
209