Шаг 78.
Решение задачи. Использование объектов

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

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

    Назовем объект-программу именем TGraphApp и разместим его в модуле GraphApp:


Unit GraphApp;
Interface
      Type
        TGraphApp = Object
                      Procedure Init;
                      Procedure Run;
                      Destructor Done;
                    End;
Implementation
                     .  .  .  .  .  .
   End.

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

Program Graph_Objects;
Uses GraphApp;
Var
     App:  TGraphApp;
Begin
    App.Init;
    App.Run;
    App.Done;
End.
Текст программы можно взять здесь.

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

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


     Var
      App: TGraphApp;       .

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


     App.Init;
     App.Run;
     App.Done;
Переменные объектного типа могут быть статическими или динамическими, то есть располагаться в сегменте данных (статические) или в "куче" (динамические). В последнем случае мы могли бы использовать такую программу:
Program Graph_Objects;
Uses GraphApp;
Type
   PGraphApp = ^TGraphApp;
Var
   App:PGraphApp;
Begin
   App:=New(PGraphApp);
   App^.Init;
   App^.Run;
   App^.Done;
   Dispose(App);
End.
Текст программы можно взять здесь.

    Для инициации динамической переменной App используется вызов функции 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;
                 Destructor 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,'D:\BP\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;

Procedure TGraphApp.Run; {Выбор и перемещение объекта по экрану}
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:=1;
                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;

Destructor TGraphApp.Done;
Begin
     CloseGraph;
End;

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 - синонимы. Введение в ООП деструкторов носит, в основном, стилистическую направленность - просто процедуру, разрушающую экземпляр объекта, принято называть деструктором. В реальной практике ООП с деструкторами обычно связывают процедуры, которые не только прекращают работу с объектом, но и освобождают выделенную для него динамическую память.

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


Предыдущий шаг Содержание Следующий шаг