Иллюстрированный самоучитель по Tirbo Pascal


Использование объектов


Идею инкапсуляции полей и алгоритмов можно применить не только к графическим объектам, но и ко всей программе в целом. Ничто не мешает нам создать объект-программу и «научить» его трем основным действиям: инициации (Init), выполнению основной работы (Run) и завершению (Done). На этапе инициации экран переводится в графический режим работы и создаются и отображаются графические объекты (100 экземпляров TPoint и по одному экземпляру TLine, TCircle, TRecf). На этапе Run осуществляется сканирование клавиатуры и перемещение графических объектов. Наконец, на этапе Done экран переводится в текстовый режим и завершается работа всей программы.

Назовем объект-программу именем TGraphApp и разместим его в модуле GraphApp (пока не обращайте внимание на точки, скрывающие содержательную часть модуля -позднее будет представлен его полный текст):

Unit GraphApp;

Interface

type

TGraphApp = object

Procedure Init;

Procedure Run;

Destructor Done; 

end;

Implementation Procedure TGraphApp.Init;

...

end;



...

end. 

В этом случае основная программа будет предельно простой:

Program Graph_0bjects;

Uses GraphApp;

var

App: TGraphApp; 

begin

App.Init;

App.Run;

App.Done 

end.

В ней мы создаем единственный экземпляр Арр объекта-программы TGrahpApp и обращаемся к трем его методам.

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

var

Арр: TGraphApp;

Получив это указание, компилятор зарезервирует нужный объем памяти для размещения всех полей объекта TGraphApp. Чтобы обратиться к тому или иному объектному методу или полю, используется составное имя, причем первым указывается не имя объектного типа, а имя соответствующей переменной:

App.Init; 

Арр.Run; 

Арр.Done;

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


Program Graph_0bjects;

Uses GraphApp;

type

PGraphApp = TGraphApp; 

var

App: PGraphApp; 

begin

App := New(PGraphApp,Init)

Арр.Run;

Арр.Done 

end;

Для инициации динамической переменной Арр используется вызов функции New. В этом случае первым параметром указывается имя типа инициируемой переменной, а вторым осуществляется вызов метода-конструктора, который, я напомню, нужен для настройки таблицы виртуальных методов. Такой прием (распределение объектов в динамической памяти с одновременной инициацией их ТВМ) характерен для техники ООП. -

Ниже приводится возможный вариант модуля GraphApp для нашей учебной программы:

Unit GraphApp; 

Interface

Uses GraphObj; 

const

NPoints = 100; {Количество точек} 

type

{Объект-программа}

TGraphApp = object

Points: array [1..NPoints] of TPoint; {Массив точек} 

Line: TLine; {Линия} 

Rect: TRect; {Прямоугольник} 

Circ: TCircle; {Окружность}

ActiveObj : Integer; {Активный объект} 

Procedure Init; Procedure Run; 

Procedure Done; Procedure ShowAll;

Procedure MoveActiveObj (dX,dY: Integer); 

end;

Implementation Uses Graph, CRT;

Procedure TGraphApp.Init;

{Инициирует графический режим работы экрана . Создает и отображает NPoints экземпляров объекта TPoint, а также экземпляры

объектов TLine, TCircle и TRect}

var

D,R,Err,k: Integer; 

begin

{Инициируем графику}

D := Detect; {Режим автоматического определения

типа графического адаптера}

InitGraph(D,R, '\tp\bgi') ; {Инициируем графический режим. Текстовая строка должна задавать путь к каталогу с графическими драйверами}

Err := GraphResult; {Проверяем успех инициации графики} 

if Err<>0 then 

begin

GraphErrorMsg (Err) ; 

Halt 

end;

{Создаем точки}

for k : = 1 to NPoints do

Points [k] .Init (Random(GetMaxX),Random(GetMaxY),Random(15)+1);

{Создаем другие объекты}

Line. Init (GetMaxX div 3, GetMaxY div 3,2*GetMaxX div 3,

2*GetMaxY div 3,LightRed);

Circ. Init (GetMaxX div 2, GetMaxY div 2, GetMaxY div 5, White);



Rect.Init(2*GetMaxX div 5,2*GetMaxY div 5 , 3*GetMaxX div 5,

3*GetMaxY div 5, Yellow);

ShowAll; {Показываем все графические объекты}

ActiveObj := 1 {Первым перемещаем прямоугольник} 

end ; { TGraphApp .Init}

{-----------}

Procedure TGraphApp .Run ;

{Выбирает объект с помощью Tab и перемещает его по экрану}

var

Stop: Boolean; {Признак нажатия Esc} 

const

D = 5; {Шаг смещения фигур}

begin

Stop := False;

{Цикл опроса клавиатуры}

repeat

case ReadKey of {Читаем код нажатой клавиши} 

#27: Stop := True; {Нажата Esc} 

#9:begin {Нажата Tab}

inc(ActiveObj);

if ActiveObj>3 then

ActiveObj := 3 

end; 

#0: case ReadKey of

#71:MoveActiveObj(-D,-D); {Влево и вверх} 

#72:MoveActiveObj( 0,-D); {Вверх} 

#73:MoveActiveObj( D,-D); {Вправо и вверх} 

#75:MoveActiveObj(-D, 0); {Влево}

#77:MoveActiveObj( D, 0); {Вправо} 

#79:MoveActiveObj(-D, D); {Влево и вниз} 

#80:MoveActiveObj( 0, D); {Вниз} 

#81:MoveActiveObj( D, D); {Вправо и вниз}

end 

end;

ShowAll; 

Until Stop 

end; {TGraphApp. Run}

{-----------}

Destructor TGraphApp . Done ;

{Закрывает графический режим} 

begin

CloseGraph 

end; {TGraphApp. Done}

Procedure TGraphApp . ShowAll ;

{Показывает все графические объекты} 

var

k: Integer;

begin

for k := 1 to NPoints do Points [k] . Show;

Line. Show;

Rect . Show;

Circ.Show 

end;

{-----------}

Procedure TGraphApp.MoveActiveObj; 

{Перемещает активный графический объект}

begin

case ActiveObj of

1: Rect.MoveTo(dX,dY);

2: Circ.MoveTo(dX,dY); 

3: Line.MoveTo(dX,dY) 

end 

end; 

end.

В реализации объекта TGraphApp используется деструктор Done. Следует иметь в виду, что в отличие от конструктора, осуществляющего настройку ТВМ, деструктор не связан с какими-то специфичными действиями: для компилятора слова destructor и procedure - синонимы. Введение в ООП деструкторов носит, в основном, стилистическую направленность - просто процедуру, разрушающую экземпляр объекта, принято называть деструктором. В реальной практике ООП с деструкторами обычно связывают процедуры, которые не только прекращают работу с объектом, но и освобождают выделенную для него динамическую память. И хотя в нашем примере деструктор Done не освобождает кучу, я решил использовать общепринятую стилистику и заодно обсудить с Вами последнее еще не рассмотренное зарезервированное слово технологии ООП.

В заключении следует сказать, что формалистика ООП в рамках реализации этой технологии в Турбо Паскале предельно проста и лаконична. Согласитесь, что введение лишь шести зарезервированных слов, из которых действительно необходимыми являются три (object, constructor и virtual), весьма небольшая плата за мощный инструмент создания современного программного обеспечения.


Содержание раздела