|
Использование объектов
Идею инкапсуляции полей
и алгоритмов можно применить не только к графическим
объектам, но и ко всей программе в целом. Ничто не мешает
нам создать объект-программу и «научить» его трем основным
действиям: инициации (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),
весьма небольшая плата за мощный инструмент создания
современного программного обеспечения.
|