Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Объектно-ориентированное программирование

..pdf
Скачиваний:
10
Добавлен:
12.11.2023
Размер:
16.61 Mб
Скачать

2.4. Динамические объекты

по указателю на объект базового класса. Поскольку реальное наполнение данного метода выполняется в других классах, в базовом класса метод Drag описывается абстрактным («пустым»).

Program SnowOb; Uses graph,dos,crt; Type

Ptr = AVt; (указатель на массив}

Vt = array [1..6] o f integer; (вектор параметров снежинки}

Snow = Object

 

(родительский класс Снежинка}

V : Ptr;

(вектор параметров снежинки}

Constructor blit;

(конструктор}

Destructor Done; virtual;

(деструктор}

Procedure

Create;

(метод создания снежинки}

Procedure

Dragyirtual;

(абстрактный метод}

End;

 

 

 

Constructor Snow.Init; Var c,i.word;

Begin

 

 

 

New(V);

(выделение памяти для поля данных }

VA[4]: =Random(14)+1;

(цвет снежинки}

VA[5]:=Random(30)+20‘; (максимальный радиус (размер снежинки)}

VA[6J:=1;

(флаг - «объект включен»}

Gettime(i, i,i,с);

( использование сотых долей секунды}

for i:-0 to с do

 

begin

(определение координат центра и радиуса снежинки}

VA[1]:=Random(GetmaxX); VA[2]:=Random(GetmaxY);

VA[3]: =Random(20)+5;

end;

 

 

End;

 

 

Destructor Snow.Done; (освобождение поля V}

Begin

if V<>nil then Dispose(V) End;

Procedure Snow.Create; Var u.real;

Begin

u: ~0; Setcolor(VA[4J);

repeat (нарисовать снежинку из 12 лучей}

Line(VA[l], VA[2],Round(VA[l]+ VA[3]*cos(u)), Round(VA[2J+ VA[3J*sin(u)));

u: =u+pi/6; until u>2*pi;

End;

Procedure Snow.Drag; Begin End;

81

2. Средства ООП в Borland Pascal 7.0

Туре

Snowl = Object(Snow) {первый потомок}

Vh.byte; { скорость изменения состояния снежинки}

Constructor Init(Vhn.Byte);

Procedure Drag; virtual; {изменение состояния снежинки}

End;

Constructor SnowUnit; Begin Snow.Init; Vh: =Vhn End; Procedure Snowl.Drag;

Var c.byte; Begin

c:= VA[4]; VA[4]:=0; {изменение цвета пера на цвет фона} Create; { изображение снежинки цветом фона - стирание} VA[4]:=c; {восстановление цвета пера}

if (VA[3] < VA[5J) and (VA[2]+ VA[5] < GetmaxY) then {если снежинка не

 

превысила максимального размера}

begin inc(VA[3], Vh); Create { то увеличение снежинки} end

else VA[б]: =0; {иначе установка флага «объект отключен»}

End;

 

Туре

 

Snow2 = Object(Snowl)

{второй потомок}

Procedure Drag; virtual;

{переопределенный метод}

End;

 

Procedure Snow2.Drag; Var c. byte;

Begin

c:= VA[4]; VA[4]:=0; {изменение цвета пера} Create; { стирание снежинки}

VA[4]:=c; {восстановление цвета пера}

if VA[2]+VA[5J < GetmaxY then {если снежинка не достигла пределов экрана}

begin inc(VA[2], Vh); { то смещение снежинки} Create end else VA[6]:=0; {иначе установка флага «объект отключен»}

End;

Type

Sdl =ASnowl;

{указатель на первый потомок}

 

Sd2 =ASnow2;

{указатель на второй потомок}

Const N=100; {максимальное количество снежинок}

Var

S : array[0..N] o fASnow; {описание массива указателей}

 

а,г,i,к,т:integer;

{вспомогательные переменные}

Begin

a:=Detect; Initgraph(a,r, ’D:\BP\BGI’) .randomize; Setbkcolor(Black); {черный фон экрана}

к: =40; {количество снежинок на экране}

for i:=0 to к do {инициализировать и вывести объекты}

82

2.5. Создание библиотек классов

begin т: =random(2); {определить тип снежинки}

ifm=l then Sfi]:=New(Sdl,Init(l)) else S[iJ:=New(Sd2,Init(l)); S[i]ACreate;

end;

repeat { цикл изменения объектов} for i: =0 to k do

beginS[iJA.Drag; {изменитьснежинку}

ifS[i]A. V*[6]=0 then {если флаг «объект отключен»} begin

Dispose(S[iJ,Done); {уничтожить объект} if random(2) =0 then SfiJ: =New(Sdl,Init(1))

else S[i]:-Neyv(Sd2,Init(l)); {создать новый}

S[i]A.Create; {нарисовать снежинку} end;

end;

until Keypressed;

for i:=1 to k do {уничтожить оставшиеся объекты}

Dispose(SfiJ,Done); End.

2.5. Создание библиотек классов

При создании библиотек классов целесообразно скрыть детали реализации классов. В этом случае описание классов можно выполнить в интерфейсном разделе модуля, а тела методов определить в разделе реализации. Также можно описывать внутренние классы, которые полностью определяются в разделе реализации. В свою очередь класс, определенный в интерфейсном разделе модуля, может иметь потомков в разделе реализации модуля.

В случае, когда модуль В использует модуль А, модуль В может опре­ делять производные классы от любого класса, экспортируемого модулем А.

Созданные модули могут поставляться в виде подключаемых (.tpu) фай­ лов с распечаткой классов, их полей и методов, определенных в интерфейсном разделе модуля. Пользователи такого модуля могут, используя механизмы наследования и полиморфизма, создавать на его основе новые классы.

Библиотека может экспортировать не только классы, но и объекты этих классов. Если необходимо экспортировать объекты, содержащие виртуальные методы, то для таких переменных в разделе инициализации можно разместить вызовы конструкторов.

Спомощью стандартной директивы private можно объявить часть полей

иметодов скрытыми от пользователей модуля. Они будут доступны только внутри модуля, где объявлен класс.

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

83

2. Средства ООП в Borland Pascal 7.0

последовательность описания классов, где используются как общедоступные, так и скрытые поля и методы.

Чтобы описание класса сделать более гибким, используют директиву public. Данная директива делает поля и методы класса общедоступными вне модуля Unit.

Структура модуля с описанием класса и экспортируемых объектов выглядит следующим образом:

Unit <имя модуля>;

Interface {интерфейсный раздел} Туре

<имя класса> = object

private

<скрытые поля>;

public

<доступные поля>;

private

<скрытые методы>;

public

<доступные методы>;

End;

 

Var объявление экспортируемых объектов класса> Im plem entation {раздел реализации}

{реализация методов}

Begin {раздел инициализации}

{вызовы конструкторов экспортируемых объектов} End.

Рассмотрим пример, в котором описание класса размещается в модуле.

П ример 2.10. Размещ ение описания класса в модуле. Для демон­ страции используем пример 2.7, в котором:

1) описание класса вынесем за пределы основной программы в отдельный модуль Unit;

2) с помощью директивы private скроем динамическое поле А (вектор атрибутов окна) метод ColorWin класса WinD.

Следует иметь в виду, что только после создания модуля Objunit.tpu скрытые поля и методы с помощью директивы private будут недоступны пользователям.

Unit ObjJUnit;

Interface {интерфейсный раздел}

Uses Crt;

Type

Vaw = array [1..6] of byte;

APtr = AVaw; {указатель на массив}

WinPtr = ''WinD; {указатель на объект}

84

2.5. Создание библиотек классов

WinD = Object

private {скрытое поле} A.APtr; {вектор атрибутов окна} public {общедоступные методы}

Constructor Init(An: Vow);

{конструктор}

Destructor Done; Virtual;

{деструктор}

Procedure MakeWin;

{изображение окна}

private {скрытый метод}

Procedure ColorWin; {установка цвета фона и символов}

End;

Implementation {раздел реализации методов}

Constructor WinD.Init;

Begin New(A); i/A=nil then begin WinD.Done; Fail end; AA:=An;

End;

Destructor WinD.Done;

Begin ifA<>nil then Dispose (A); End; Procedure WinD.MakeWin;

Begin Window(AA[1],A [2],AA[3],AA[4]); Self.ColorWin End; Procedure WinD.ColorWin;

Begin TextbackGround(AA[5]); TextColor(AA[6]); Clrscr End; End.

Текст основной программы, использующий модуль ObjUnit:

Program ModOb;

Uses ObjUnit; {подключение созданного модуля}

Var

V : WinPtr; {указатель на динамический объект} А :Vaw; {вектор атрибутов окна}

Begin

А[1]:=1; А[2]:=1; А[3]:=80; А[4]:=25; {координаты окна}

А[5]:=7; А[6]:=1; {цвет фона и символов} New(V,Init(A)); {размещение динамического объекта}

if V=nil then Halt(2);

VA.MakeWin; {применение доступных методов} Dispose(V,Done) {освобождение объекта и полей}

End.

Попытка обратиться непосредственно к полям класса с помощью составных имен, например для их инициализации:

УЛ.АЛ[1]:=1; УЛ.АЛ[2]:=1; УЛ.АЛ[3]:=60; УЛ.АЛ[4]:=20; УЛ.АЛ[5]:=3; УЛ.АЛ[1]:=2;

85

2. Средства ООП в Borland Pascal 7.0

приведет к ошибке 44 (Field identifier expected - «ожидается имя поля или метода класса»). Обусловлено это тем, что стандартная директива private делает недоступными поля класса WinD из основной программы примера 2.7.

Аналогичная ошибка обнаружится при прямом обращении:

VA.ColorWin;

Метод ColorWin будет также недоступен из основной программы, как и поля класса Win. С другой стороны, данный метод можно использовать внутри модуля, в частности в методе MakeWin.

2.6. Композиция и наполнение

Для реализации объектов, находящихся между собой в отношении включения (см. рис. 1.15), могут использоваться механизмы наследования или композиции.

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

Композиция реализуется включением в класс о б ъ е к т н ы х п о л е й и применяется в тех случаях, когда наследование по каким-либо причинам невозможно или нецелесообразно.

Borland Pascal 7.0 предоставляет возможность использования в классах объектных полей. Рассмотрим это на примере.

П ример 2.11. Использование объектных полей. Рассмотрим прог­ рамму, в которой класс Выводимая строка (OutS) объявлен как часть класса Окно (Win). Это реализовано включением в структуру класса Win объектного поля Ob_Field.

Program Compos; Uses Crt;

Type

OutS = Object (класс Выводимая строка} S.string; (строка вывода}

Cs.byte; (цвет символов строки}

Procedure Init(Sn:string;Csn:byte); (инициализация полей} Procedure Print; (вывод строки}

End;

Procedure OutS.Init; Begin S: =Sn; Cs: =Csn End; Procedure OutS.Print; Begin Textcolor(Cs); Write(S) End; Type

Win = Object (класс Окно} X1,Y1,X2,Y2, (координаты окна} Cfibyte; (цвет фона}

86

2.6. Композиция и наполнение

Ob_Field: OutS; {статическое объектное поле}

Procedure Init(Xnl, Ynl,Xn2, Yn2,Cfn.byte;Obn:OutS); Procedure MakeWin; {изображение окна}

End; Procedure Win.Init;

Begin Xl:=Xnl; Yl:=Ynl; X2:=Xn2; Y2:=Yn2; Cf:=Cfti; Ob_Field:=Obn; {инициализация объектного поля}

End;

Procedure Win.MakeWin; Begin

Window(XI, Y1,X2,Y2); Textbackground(Cj); Clrscr;

Ob_Field.Print; {обращение к методу Print класса OutS как к методу поля класса Win}

End;

Var V:Win; FrOutS; {объявление экземпляров классов}

Begin

F.Init( 'Объектное поле 1); {инициализировать объекты}

VInit(20,10,35,10,0,F); {F - параметр-объект}

V.MakeWin; {изобразить окна и вывести строку}

Readkey;

End.

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

Пример 2.12. Использование полей - указателей на объекты.

Рассмотрим программу, в которой автоматически генерируется заданное количество окружностей (объектов), перемещающихся по линейному закону

впределах определенной зоны экрана (рис. 2.6).

Впрограмме осуществляется анализ на столкновение всех окружностей друг с другом и с зоной. При столкновении окружности меняют направление движения, «оттолкнувшись» друг от друга.

Диаграмма объектов для данного примера представлена на рис.2.7. Для реализации объектов программы потребуются классы: Zona (зона) и Cir (окружность). Класс Zona будет включать поля, определяющие размер зоны на экране, поле N - для хранения количества окружностей и поле Р - массив указателей на объекты класса Cir. Он также будет включать методы инициализации Init, изображения зоны Out и перемещения окружностей Run. Удобно определить внутренний метод Control, который будет определять наличие столкновений всех созданных объектов-окружностей друг с другом и зоной (рис.2.8).

Класс Cir существенно проще. Он должен содержать поля, определяющие его местонахождение, радиус и цвет, а также метод перемещения и внутренний

87

2. Средства ООП в Borland Pascal 7.0

Рис. 2.6. Вид экрана при выполнении программы «Окружности»

метод изображения. Запрос зоны о местоположении окружности можно реализовать простым обращением к соответствующим полям объекта.

Рассмотрим работу программы, акцентируя внимание на использовании полей указателей на объекты. Вначале задаются параметры зоны и определяется количество размещаемых окружностей. Далее инициализируются объекты типа Cir и объект типа Zona. В результате полю Р объекта Z будет передан массив указателей на объекты типа Cir.

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

г

 

 

\

Основная программа

 

^__________________________ J

Изобразить окружности

 

^Двигать окр•ужности

/-------------

9

\

 

V

Зона

 

 

1

 

W

 

 

( Окружность 1 J ^ Окружность 2 J ...

^Окружность М J

Рис. 2.7. Диаграмма объектов программы «Окружности»

1 —местоположение и размер окружности; 2 —переместить окружность

88

2.6. Композиция и наполнение

Поля: X, Y - координаты

Поля: X, Y - координаты левого

центра окружности

верхнего угла

R, С - радиус и цвет

Lx, Ly - длина зоны по осям

окружности

Р - массив указателей типа 'ХЛг

 

N - количество окружностей

Методы: Init - инициализация

Методы: Init - инициализация

полей

Out - вывод

Out - изображение

Run - движение

окружности

окружностей

Move - перемещение

Control - проверка

окружности

столкновений

Рис. 2.8. Диаграмма классов программы «Окружности»

Такое управление становится возможным благодаря использованию указателей, переданных объекту типа Zona.

Program MoveCir;

Uses graph, crt;

Type

Cp=ACir;

{указатель на класс}

Cir=Object

{класс Окружность}

X, Y,R,С,

{координаты центра, радиус и цвет окружности}

Dx,Dy:integer;

{шаг перемещения}

Constructor Init(Xn, Yn,Rn,Cn,Dxn,Dyn:integer); {конструктор}

Procedure Out;

{изображение окружности}

Procedure Move; {перемещение окружности}

End; Constructor Cir.Init;

Begin R: =Rn; C: =Cn; Dx: =Dxn; Dy: =Dyn; X: =Xn; Y:=Yn End; Procedure Cir.Out;

Begin Setcolor(C); Circle(X, Y,R) End; Procedure Cir.Move;

Var cc:integer;

 

Begin

cc:=C; C:=Getbkcolor; Out; C:=cc; X:=X+Dx; Y:=Y+Dy; Out;

End;

 

 

Type

 

 

mascp=array [1..100] ofCp; {массив указателей}

Zona=Object

{класс Зона}

X, Y.Lx.Ly,

{координаты верхнего левого угла и размеры зоны}

C:\vord;

{цвет зоны}

P.mascp;

{поле-массив указателей}

N:byte;

 

{количество окружностей}

89

2. Средства ООП в Borland Pascal 7.0

Constructor Init(Xn, Yn,Lxn,Lyn,Cn:word;Pn:mascp;Nn:byte); Procedure Out; {изображение зоны}

Procedure Control; {проверка объектов на столкновение} Procedure Run; {перемещение окружностей}

End;

Constructor ZonaJnit;

Begin X:=Xn; Y:=Yn; Lx:=Lxn; Ly:=Lyn; C:=Cn; P:-Pn; N:=Nn End; Procedure Zona. Out;

Begin Setcolor(C); Rectangle(X, Y,X+Lx, Y+Ly) End; Procedure Zona. Control;

{Прогноз на столкновение двух окружностей}

Procedure CrCr(Pl,P2:Cp); Var wx,wy:real;

Begin wx: =PlA.X+PlA.Dx-P2A.X-P2A.Dx; wy: =P1A. Y+PlA.Dy-P2A.Y-P2A.Dy;

if (sqrt(sqr(wx)+ sqrfwy)) <= P2A.R+P1A.R) then begin {смена направления перемещения}

P lA.Dx: =-PlA.Dx; P2A.Dx: =-P2A.Dx;

P lA.Dy: =-PlA.Dy; P2A.Dy: =-P2A.Dy;

end;

End;

{Прогноз на столкновение окружности с зоной}

Procedure CrZn(P:Cp); Begin

if (PA.X+PA.Dx-PA.R <=X)or (PA.X+PA.Dx+PA.R >=X+Lx) then PA.Dx: =-PA.Dx; {смена направления движения по X}

if(PA.Y+PADy-PA.R <=Y) or (PAY+PA.Dy+PA.R >= Y+Ly) then PA.Dy: =-PA.Dy; {смена направления движения no Y}

End;

Var ij. integer; Begin

{Проверка на столкновение всех окружностей друг с другом} for i:=l to N-l do forj:=i+l to N do CrCr(P[iJ,P[jJ);

{Проверка на столкновение с зоной всех окружностей} for i:=1 to N do CrZn(P[iJ);

End;

Procedure Zona.Run; Var k:integer;

Begin Control; {вызвать метод проверки на столкновение} for к:=1 to п do P[k]A.Move;

End;

{ основная программа}

Var Z.Zona; {объект класса Zona}

90