Учебник по программированию.

Первые шаги. Язык программирования PascalABC.

 

Предыдущий параграф Назад в содержание Следующий параграф


§29. Оставшиеся темы по ООП. Подготовка объектов для игры «Гонки».

Статические члены класса.

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

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


type TMath = class

       class Pi:=3.14;

       class function summ(x,y:real):real;

         begin

           summ:=x+y;

         end;

      end;  


var r,r1:real;


begin

  r1:=TMath.Pi;

  r:=TMath.summ(r1,4);

  writeln(r);

end.

___________________________________________

7.14


Через объект к статическому члену обратиться нельзя. Однако внутри класса экземплярные члены могут обращаться к статическим без ограничения. Статические члены могут обращаться только к статическим.

В отличии от экземплярных членов, статические методы, поля и свойства создаются при создании программы и существуют на протяжении всей её работы. Экземплярные члены создаются в момент создания объекта и существуют, пока существует объект. Объект существует в том месте кода, где он создан, и который ограничен словами begin и end. Например, если объект создаётся в теле подпрограммы, то при выходе из неё он будет уничтожен.

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

То же самое может быть и с методом. Если объявить его статическим, то не придётся создавать весь объект для вызова одного метода.

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

Рекомендую самостоятельно поэкспериментировать с данной темой. Думаю, особых затруднений возникнуть не должно.


Перегрузка методов.

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


type TC = class

       function summ(x,y:integer):integer;

         begin

           summ:=x+y;

         end;

       function summ(x,y:real):real;

         begin

           summ:=x+y;

         end;

     end;

var C:TC:=new TC;

begin

  writeln(C.summ(1,2));

  writeln(C.summ(1.3,2.3));

end.

____________________________________________

3

3.6


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


Заключение по теме ООП.

В рамках данной книги ряд тем по ООП не было освящено. Следующие три темы необходимо изучить самостоятельно: «Предварительное описание классов», «Инициализаторы полей». Находятся они в разделе справки: «Справочник по языку -> Классы». В учебнике они не приведены  потому, что, на мой взгляд, они не сложные и могут быть изучены самостоятельно.

Если вы внимательно полистаете данный раздел справки, то увидите ещё некоторые незнакомые темы. Думаю, что на данном этапе развития вы вряд ли будете ими пользоваться, поэтому пока изучать их нет смысла.

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


Подготовка объектов для игры «Гонки».

В 19 параграфе была написана игра «Гонки» с использованием модуля CRT. В последнем параграфе будет предложено переписать данную игру с использованием ООП. Сейчас предлагаю подготовить классы объектов, которые могут быть использованы в этой игре. Описание классов этих объектов поместим в отдельный модуль.

Для начала произведём словесное описание всего того, что должно быть в игре.

Так же как и в предыдущей версии игры, у нас будет трасса, по которой будет двигаться машинка. Трасса будет иметь тот же вид, как и рисунок в программе Risunki из 13 параграфа. Соответственно процедуры рисования препятствий можно взять из модуля MRisunki. Конечно, слово трасса подобрано здесь не совсем удачно, т.к. этот рисунок больше похож на просёлочную дорогу, однако для упрощения предлагаю называть её именно трассой.

Эффект движения будет достигаться за счёт движения препятствий, которые будут двигаться сверху вниз на встречу машинке. Машинка должна их объезжать, перемещаясь влево и вправо. Количество объезженных препятствий должно считаться. Со временем скорость движения трассы должна увеличиваться.

Прежде чем читать дальше откройте и пробегитесь глазами по тексту модуля MRisunki, который был создан в разделе решения задач в 21 параграфе (решение 2-ой задачи), это поможет понимать дальнейший текст книги.

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

  • координаты левого верхнего угла;
  • размеры объекта;
  • конструктор;
  • метод стирания объекта.

Различными будут только методы рисования объектов. Поэтому вначале опишем базовый класс, а затем производные, в которых опишем методы рисования различных объектов.

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

  • координаты объекта оформим в виде свойств. При изменении которых, объект будет автоматически перерисовываться. А так же будет автоматически происходить контроль координат, что бы они не вышли за рамки трассы. Это упростит программирование самой игры.
  • метод рисования объекта будет вызываться как при изменении свойств координат так и в конструкторе, однако он будет описан только в классе потомке. Поэтому в базовом классе этот метод должен быть помечен словом abstract.

Базовый класс назовём TBase. После его описания можно приступить к описанию различных производных классов, в которых будут различные методы рисования. Эти методы почти в неизменном виде можно скопировать из программы Risunki. Для упрощения программирования самой игры, в начало этого метода можно добавить метод стирания объекта. Производным классам можно присвоить следующие идентификаторы: TDerevo, TDom, TMelnica, TMashina.

Далее приведён код получившегося модуля, а так же программы, с помощью которой проверяется правильность работы написанных классов:


unit MObjects;

uses GraphABC,MRisunki;


const X1=100; X2=540; Y1=0; Y2=480;//Координаты трассы


type TBase = class

       polex,poley:integer;//координаты левого верхнего угла объекта

       shir:=30; vis:=40;//размеры объекта

       constructor (xx,yy:integer);

         begin

           polex:=Koor(xx,X1,X2,shir);

           poley:=Koor(yy,Y1,Y2,vis);

           clear;

           Draw;

         end;

     //Метод не даёт координатам выйти за пределы трассы

       function Koor(a,A1,A2,Razm:integer):integer;

         begin 

           if (a>A1) and (a<(A2-Razm)) then 

              Koor:=a

             else

               begin

                 if (a<=A1) then Koor:=A1+1;

                 if (a>=(A2-Razm)) then Koor:=A2-1-Razm;

               end;

         end;

       procedure clear;//стирание объекта

         begin

           MRisunki.Clear(x,y);

         end;

       procedure SetX(xx:integer);

         begin

           clear;

           polex:=Koor(xx,X1,X2,shir);

           Draw;

         end;

       procedure SetY(yy:integer);

         begin

           clear;

           poley:=Koor(yy,Y1,Y2,Vis);

           Draw;

         end;

       procedure Draw; abstract;

       property x:integer read polex write SetX;

       property y:integer read poley write SetY;

     end;


TDerevo = class (TBase)

  procedure Draw;override;//Рисует дерево

    begin

      DrawDerevo(x,y);

    end;

end;



TDom = class (TBase)

  procedure Draw;override; //Рисует домик

    begin

      DrawDomik(x,y);

    end;

end;


TMelnica = class (TBase)

  procedure Draw;override;//Рисует мельницу

    begin

      DrawMelnica(x,y);

      DrawLopasti(x,y);

    end;

end;


TMashinka = class (TBase)

  procedure Draw;override;

    begin

      DrawCar(x,y);

    end;

end


end.

==========================================================

program Proverka;

uses GraphABC,MObjects;


var Dom:TDom;

    Derevo:TDerevo;

    Melnica:TMelnica;

    Mashinka:TMashinka;

begin

//Рисуем трассу

  SetBrushColor(clLimeGreen);

  FillRect(X1,Y1,X2,Y2);

//Рисуем дом, дерево и машинку

  Dom:=new TDom(20,50);

  Derevo:=new TDerevo(100,100);

  Mashinka:=new TMashinka(300,150);

//Рисуем мельницу

  Melnica:=new TMelnica(400,50);

  for var i:=1 to 5 do

    begin

      Melnica.clear;

      Melnica.Draw;

      Sleep(200);

    end;

//Перемещаем объекты вниз

  for var j:=1 to 5 do

    begin

      Dom.y:=Dom.y+2;

      Derevo.y:=Derevo.y+2;

      Melnica.y:=Melnica.y+2;

      Mashinka.y:=Mashinka.y+2;

      for var i:=1 to 10 do

        begin

          Melnica.clear;

          Melnica.Draw;

          Sleep(200);

        end;

    end;

//Перемещаем объекты вверх

................................

//Перемещаем объекты вправо

................................

//Перемещаем объекты влево

................................

end.


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

В заключение хочется сделать следующее замечание по получившемуся тексту кода: если не брать во внимание код находящийся в модуле MObjects, и говорить только о самой программе  Proverka, то, на мой взгляд, благодаря возможностям ООП код получился проще и понятнее. Попробуйте представить себе, сколько всего необходимо было бы написать для реализации точно такой же программы без использования ООП. Думаю, что после этого вы со мной согласитесь.


В данном параграфе мы узнали, что такое статические члены класса и научились перегружать методы. Провели обзор оставшихся тем по ООП, которые необходимо изучить самостоятельно. Создали модуль MObjects в котором содержаться классы, которые могут быть использованы для написания игры «Гонки».


Задачи.

1. В 17 параграфе при решении задач необходимо было создать модуль Mat. Переписать этот модуль в виде класса. Т.е. создать класс TMat со статическими методами из модуля Mat. Написать программу, демонстрирующую работу получившегося класса.

2. В 26 параграфе при решении задач был создан класс TMyach. Создать класс потомок и дописать в него конструктор, в который передаются параметры мячей. Т.е. они должны передаваться конструктору, а не создаваться автоматически случайным образом. Написать программу, демонстрирующую работу этого класса.

3. Изучить самостоятельно следующие темы: «Предварительное описание классов» и «Инициализаторы полей». Придумать и написать примеры их использования.

4. Создать программу, демонстрирующую принцип полиморфизма. Программу создать с использованием модуля MObjects. Она должна работать следующим образом: в теле бесконечного цикла случайным образом выбирается число от одного до четырёх. В зависимости от выбранного числа, выбирается конструктор одного из производных классов для создания объекта класса TBase. Координаты, передаваемые в качестве параметров в конструктор, так же должны выбираться случайным образом.

5. Усовершенствовать  предыдущую программу, добавив в класс TBase статическое поле count:integer (счётчик), в котором будет содержаться количество созданных объектов. Каждый раз при создании нового объекта счётчик должен увеличиваться на единицу. Вывести на экран содержимое данного поля.


Решение.

1.2.3. Эти задачи решите самостоятельно.

4.5. Решения обоих заданий приведены в одном тексте кода:

unit MObjects;

...............................

type TBase = class

       class count:integer:=0;

...............................

==============================

program Polimorfizm;

uses GraphABC,MObjects;


var Ob:TBase;

    var i,x,y:integer


begin

  TextOut(1,1,'Создано');

  TextOut(1,20,'объектов -');

  While true do

    begin

      i:=Random(1,4);

      x:=Random(1,640);

      y:=Random(1,480);

      case i of

        1:Ob:=new TDerevo(x,y);

        2:Ob:=new TDom(x,y);

        3:Ob:=new TMelnica(x,y);

        4:Ob:=new TMashinka(x,y);

      end;

      TBase.count:=TBase.count+1;

      SetPenColor(clBlack);

      SetBrushColor(clWhite);

      TextOut(20,40,IntToStr(TBase.count));

      Sleep(500);

    end

end.



Предыдущий параграф Назад в содержание Следующий параграф