{если в объекте есть поля X и Y, то эти имена нельзя использовать в
качестве формальных параметров. Выход из этой ситуации — добавлять
символ _ после имени, чтобы было понятно, к использованию какого поля
объекта относится передаваемый параметр}
end;
tCircle=
object(tDot)
...
end;
procedure... {описание процедур и функций, видимых в других модулях}
var... {описание глобальных переменных, видимых в других модулях}
секция реализации
implementation
uses m2,m3; {модули m2 и m3 доступны процедурам и функциям области
реализации}
var...; {описание глобальных переменных, доступных только в данном модуле}
описание реализации методов объектов
procedure tDot. Show;
begin
...
end;
procedure tDot. Hide;
begin
...
end;
procedure tDot. Move(dX, dY:Integer);
begin
...
end;
procedure tDot. MoveTo(NewX, NewY:Integer);
begin
...
end;
procedure tDot. Init(X_,Y_:Integer);
begin
X:=X_;
Y:=Y_;
tDot. Hide;
end;
procedure tCircle...;
begin
...
end;
...
begin {секция инициализации — обычно пустая}
end.
Декларация использования других модулей в области интерфейса аналогична вставлению соответствующих модулей (а на самом деле ссылки на модуль) вместо этой декларации. Поэтому в интерфейсе модуля m1 нельзя писать uses myFigure, т. к. тогда получилось бы, что в модуль myFigure вложен модуль m1, который, с свою очередь, вложен в модуль myFigure, т. е. myFigure вложен сам в себя. Для раздела реализации подобных ограничений нет, и в модулях m2 и m3 может стоять ссылка uses myFigure. В интерфейсе всегда надо ссылаться на модули, в которых описаны классы-прародители, а также на те, из которых надо брать определенные в них типы для использования в интерфейсе своего модуля.
Классы, экземпляры класса, наследование, иерархия.
Объектный тип (например, tDot) называется классом. Переменная данного типа — объект или, что то же самое, экземпляр класса. То есть при задании
Var dDot:tDot
переменная dDot – это объект.
tDot — тип-прародитель (класс-прародитель),
· tCircle=
object(tDot) {тип-наследник (класс-наследник)};
...
end;
· tRectangle=
object(tDot) {другой наследник};
...
end;
Тут tCircle и tRectangle — прямые потомки tDot (т. е. он их прямой прародитель) и т. п.
Далее можно определить другие классы-наследники:
· tTriangle=
object(tDot)
...
end;
· tFilledTriangle=
object(tTriangle)
...
end;
· tFilledCircle=
object(tCircle)
...
end;
· tRedCirlce=
object(tCirlce)
...
end;
· tGreenCirlce=
object(tCirlce)
...
end;
· tFilledRectangle=
object(tRectangle)
...
end;
Иерархия этих классов:
Суть наследования заключается в том, что потомки могут использовать поля данных и методы прародителей без определения этих полей и методов в классе потомка. Говорят, что поля данных и методы прародителей наследуются потомками.
Пример:
type
tDot=
object
active:Boolean;
X, Y:Integer;
procedure Show;
procedure Hide;
procedure MoveBy(dX, dY:Integer);
procedure MoveTo(NewX, NewY:Integer);
procedure Init(X_,Y_:Integer);
end;
tCircle=
object(tDot)
radius:Real;
procedure Show;
procedure Hide;
procedure MoveBy(dX, dY:Integer);
procedure MoveTo(NewX, NewY:Integer);
procedure Init(X_,Y_,R_:Integer);
end;
var aDot: tDot;
aCircle: tCircle;
В теле объекта aCircle можно пользоваться полями X, Y, active, как если бы они были определены в tCircle, а не в tDot. При этом они никакого отношения не имеют к объекту aDot, т. е. к tDot. X, aDot. Y, aDot. Active. Это aCircle. X, aCircle. Y, aCircle. Active. Но по своей структуре они дублируют соответствующие поля объекта aDot.
А вот вызов методов aCircle. Show, aCircle. Hide и aCircle. Move(...) идет по-другому. Реально это tCircle. Show, tCircle. Hide и tCircle. Move(...), а методы для aDot — это tDot. Show, tDot. Hide и tDot. Move(...), "настроенные" на конкретный экземпляр класса, то есть "знающие", кого показывать, скрывать, перемещать.
Процедура Init переопределена так, что теперь надо задавать радиус, в отличие от прародителя:
Процедура Init в tCircle переопределена так, что теперь надо задавать радиус, в отличие от прародителя. Говорят, что она перекрывает соответствующий метод прародителя.
procedure tCircle. Init(X_,Y_,R_:Integer);
begin
tDot. Init(X_,Y_);
R:=R_;
end;
procedure tCircle. Hide;
var TmpColor:Word;
begin
...{нарисовать окружность цветом фона}
active:=false;
end;
procedure tCircle. MoveBy(dX, dY:Integer);
begin
Hide;
X:=X+dX;
Y:=Y+dY;
Show;
end;
procedure tCircle. MoveTo(NewX, NewY:Integer);
begin
Hide;
X:=NewX;
Y:=NewY;
Show;
end;
Замечание: в принципе типы полей данных в потомках переопределять нельзя (по сравнению с прародителями). Все поля, имеющиеся у прародителей, имеются и у всех их потомков, плюс новые поля, которые дополнительно определены для потомков. Но начиная с Delphiи в Object PASCAL была введена возможность перекрытия полей данных.
Мы пока рассматриваем только так называемые статические методы. Они вызываются так, как скомпилированы для соответствующего типа; их можно переопределять в потомках. Но зато каждый раз надо дублировать текст: процедура Move для tCircle абсолютно аналогична процедуре Move для tDot. Если бы мы не описали процедуру tCircle. Move, благодаря наследованию вызвалась бы tDot. Move! И показалось бы перемещение точки, а не окружности. Существуют также виртуальные методы, которые позволяют обеспечить большую гибкость, чем статические. Но о них речь пойдет позже.
Часто используют типы, называемые абстрактными, у которых единственное назначение — "растить" от них наследников "в разные стороны", чтобы иметь в наследниках соответствующие поля и методы. При этом изменение реализации такого класса автоматически "бесплатно" меняет поведение всех потомков. А вот интерфейс абстрактных классов стараются не менять, иначе надо переписывать почти всю программу — менять все вызовы соответствующих методов в потомках. У абстрактных классов не бывает экземпляров объектов (т. е. переменных такого типа — реально вызываемых объектов).
Пример:
type
tLocation=
object
X, Y:Integer;
procedure Init(X_,Y_:Integer);
function GetX:Integer;
function GetY:Integer;
end;
tDot=
object(tLocation)
active:Boolean;
procedure Init(X_,Y_:Integer);
procedure Show;
procedure Hide;
procedure MoveBy(dX, dY:Integer);
end;
тут tLocation — абстрактный тип; у него не будет экземпляров. Зато из любого потомка можно будет вызывать методы GetX и GetY.
Пример объектно-ориентированной программы: движение фигур по экрану.
unit Figures;
interface
uses…;
type
tLocation=
object
X, Y:Integer;
procedure Init(X_,Y_:Integer);
function GetX:Integer;
function GetY:Integer;
end;
tpDot=^tDot; {тип 'указатель на экземпляр класса tDot'. См. далее}
tDot=
object(tLocation)
aсtive:Boolean;
constructor Init(X_,Y_:Integer); {конструктор. См. далее}
destructor Done;virtual; {деструктор. См. далее}
procedure Show;virtual; {виртуальный метод. См. далее}
procedure Hide;virtual;
function IsActive:Boolean;
procedure MoveBy(dX, dY:Integer);virtual;
procedure MoveTo(newX, newY:Integer);virtual;
end;
tpCircle=^tCircle; {указатель на экземпляр tCircle}
tCircle=
object(tDot)
R:Integer;
constructor Init(X_,Y_:Integer;R_:Integer);
procedure Show;virtual;
procedure Hide;virtual;
procedure ChangeRadius(dR:Integer);virtual;
end;
{——— секция реализации ———}
implementation
{методы tLocation}
procedure tLocation. Init(X_,Y_:Integer);
begin
X:=X_;
Y:=Y_;
end;
function tLocation. GetX:Integer;
begin
GetX:=X;
end;
function tLocation. GetY:Integer;
begin
GetY:=Y;
end;
{методы tDot}
constructor tDot. Init(X_,Y_:Integer);
begin
tLocation. Init(X_,Y_);
active:=false;
end;
destructor tDot. Done;
begin
Hide;{это tDot. Hide для объекта класса tDot}
end;
procedure tDot. Show;
begin
… {нарисовали текущим цветом}
active:=true; {пометили состояние как активное}
end;
procedure tDot. Hide;
begin
… {нарисовали цветом фона}
active:=false; {пометили состояние как неактивное}
end;
function tDot. IsActive:Boolean;
begin
Result:=active;
end;
procedure tDot. MoveBy(dX, dY:Integer);
begin
Hide;{ это tDot. Hideдля объекта dDot, tCircle. Hideдля aCircle и т. д.}
X:=X+dX;
Y:=Y+dY;
Show; {tDot. Show для aDot, tCircle. Show для aCirle и т. д.
end;
procedure tDot. MoveTo(newX, newY:Integer);
begin
Hide;
X:=newX;
Y:=newY;
Show;
end;
{методы tCircle}
constructor tCircle. Init(X_,Y_,R_:Integer);
begin
tDot. Init(X_,Y_); {инициализация прародителя}
R:=R_;
end;
procedure tCircle. Show;
begin
…{ рисование окружности }
Active:=true;
end;
procedure tCircle. Hide;
var TmpColor:tColor;
begin
TmpColor:=; {запомнили текущий цвет рисования}
…{установили текущим цвет фона}
…{нарисовали окружность цветом фона, т. е. скрыли}
…{восстановили текущий цвет рисования}
Active:=false; {пометили состояние как неактивное}
end;
procedure tCircle. ChangeRadius(dR:Integer);
begin
Hide;
R:=R+dR;
if R<0 then R:=0; {радиус не может быть отрицательным}
Show;
end;
end.
В качестве примера использования модуля Figures напишем программу, в которой используются классы модуля Figures, а также добавляется наследник класса tCircle. При этом для него статический метод Init и виртуальные методы Show и Hide переписываются заново (как говорят — перекрываются), а все другие методы (например, moveBy) наследуются.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |


