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