Класс Дорога
Объектно-ориентированная программа начинается с описания классов объектов. Класс в программе — это новый тип данных. Как и структура (см. § 39), класс — это сложный тип данных, который может объединять переменные различного типа в единый блок. Однако, в отличие от структуры, класс содержит не только данные, но и методы работы с ними (процедуры и функции).
В нашей программе самый простой класс — это Дорога. Объекты этого класса имеют два свойства: длину (англ. length), которая может быть вещественным числом, и ширину (англ. width) — количество полос, целое число. Для хранения значений свойств используются переменные, принадлежащие объекту, которые называются полями.
Поле — это переменная, принадлежащая объекту.
Значения полей описывают состояние объекта, а методы — его поведение.
Описание класса Дорога в программе на объектной версии Паскаля (здесь имеется в виду FreePascal или Delphi) выглядит так:
type TRoad = class
Length: real;
Width: integer; end;
Эти строки вводят новый тип данных — класс TRoad1, т. е. сообщают компилятору, что в программе, возможно, будут использоваться объекты этого типа. При этом в памяти не создаётся ни одного объекта. Это описание похоже на чертёж, по которому в нужный момент можно построить сколько угодно таких объектов.
Буква «Т» в начале названия класса — это сокращение от слова type.
Если мы хотим работать с объектом класса TRoad, в программе нужно объявить соответствующую переменную:
var road: TRoad;
Однако и это ещё не объект, а ссылка (указатель), т. е. переменная, в которой можно сохранить адрес любого объекта класса TRoad. Чтобы создать сам объект в памяти, нужно вызвать специальный метод Create, который называется конструктором. Адрес нового объекта записываем в переменную road:
road:=TRoad.Create;
Созданный объект относится к классу TRoad, поэтому его называют экземпляром класса TRoad.
При описании класса мы ничего не говорили о методе Create. Он добавляется ко всем классам по умолчанию, при его вызове все переменные объекта заполняются нулями.
Конструктор — это метод класса, который вызывается для создания объекта этого класса.
Свойства дороги можно изменить с помощью точечной нотации, с которой вы познакомились, работая со структурами:
road.Length:=60; road.Width:=3;
Полная программа, которая создаёт объект «дорога» (и больше ничего не делает), выглядит так:
{$mode objfpc} type TRoad = class
Length: real;
Width: integer; end;
var road: TRoad; begin
road:=TRoad.Create; road.Length:=60; road.Width:=3 end.
Хотя стандарта на этот счёт нет, так сделано во всех объектных реализациях Паскаля.
Строка {$mode objfpc} по форме похожа на комментарий, потому что заключена в фигурные скобки. Однако для компилятора это команда перейти в режим работы с объектами (англ. mode — режим; object — объект; FPC — Free Pascal Compiler — свободно распространяемый компилятор Паскаля).
Начальные значения полей можно задавать прямо при создании объекта. Для этого нужно добавить в описание класса новый конструктор. Конструктору будет передаваться два параметра — начальные значения длины и ширины дороги:
type TRoad = class
Length: real;
Width: integer;
constructor Create(lengthO: real;
widthO: integer)
end;
Реализация (программа) конструктора может выглядеть так:
constructor TRoad.Create(lengthO: real;
widthO: integer);
begin
if length0>0 then
Length:=lengthO else Length:=1; if width0>0 then
Width:=widthO else Width:=1;
end;
Здесь проверяется правильность переданных параметров, чтобы по ошибке длина и ширина дороги не оказались нулевыми или отрицательными. Теперь создавать объект будет проще:
road:=TRoad.Create (60, 3) ;
Длина этой дороги — 60 единиц, она содержит 3 полосы.
Таким образом, класс выполняет роль «фабрики», которая «выпускает» (создаёт) объекты «по чертежу» (описанию класса) при вызове конструктора.
Конечно, в реальной программе при передаче неправильных данных нужно выдавать сообщение об ошибке.
Машина
Теперь можно описать класс Машина (в программе назовём его ТСаг). Объекты класса ТСаг имеют три свойства и один метод — процедуру move. Координата X и скорость V — это вещественные значения, а номер полосы Р — целое, type ТСаг = class
X, V: real;
Р: integer; road: TRoad; procedure move;
constructor Create(roadO: TRoad;
pO: integer; vO: real);
end;
Так как объекты-машины должны обращаться к объекту «дорога», в область данных включено дополнительное поле road. Конечно, это не значит, что в состав машины входит дорога. Напомним, что это только ссылка, и сразу после создания объекта-машины нужно записать в неё адрес заранее созданного объекта «дорога». Эту привязку удобно сделать прямо в конструкторе, при создании объекта. Заодно мы определяем полосу движения и скорость, а начальная координата X автоматически устанавливается равной нулю:
constructor ТСаг.Create(roadO: TRoad;
pO: integer; vO: real);
begin
road:=roadO; P:=pO; V:=vO; end;
Теперь займёмся реализацией (программированием) метода move (англ. move — двигаться). В этом методе нужно вычислить новую координату X машины и, если она находится за пределами дороги, установить её в ноль (машина появляется слева на той же полосе). Изменение координаты при равномерном движении описывается формулой
X = Х0 + V • At,
где Х0 и X — начальная и конечная координаты, V — скорость, a At — время движения. Вспомним, что любое моделирование физических процессов на компьютере происходит в дискретном времени, с некоторым интервалом дискретизации. Для простоты мож
но измерять время в этих интервалах, а за скорость V принять расстояние, проходимое машиной за один интервал. Тогда метод move, описывающий изменение положения машины за один интервал (At = 1), может выглядеть так:
procedure TCar.move; begin
X:=X+V;
if X > road.Length then X:=0; end;
Основная программа
В основной программе объявим массив объектов-машин:
const N=3;
var cars: array [1..N] of TCar;
Как вы помните, это ещё не объекты, а ссылки — переменные, в которые можно записать адреса объектов класса ТСаг. Теперь нужно создать сами объекты:
var i: integer;
for i:=l to N do
cars[i]:=TCar.Create(road, i, 2.0*i);
При вызове конструктора задаются три параметра: адрес объекта «дорога» (его нужно создать до выполнения этого цикла), номер полосы и скорость. В приведённом варианте машина на полосе с номером i движется со скоростью 2i единиц за один интервал моделирования.
Сам цикл моделирования получается очень простой: на каждом шаге вызывается метод move для каждой машины:
repeat
for i:=l to N do cars[i].move; until keypressed;
Этот цикл закончится тогда, когда пользователь нажмёт любую клавишу и функция keypressed, расположенная б модуле Crt, вернет значение True.
Полностью основная программа выглядит так:
us е: s С: лг -t ;
cj.onst- 1ST = 3 р
-v-3t:rr 2COSLci: UTESlo sl ci;
cars: array [1..N] of TCar; i: integer;
begin
road:=TRoad.Create(60, N) ; for i:=l to N do
cars[i]:=TCar.Create(road, i, 2.0*i); repeat
for i:=l to N do cars[i].move; until keypressed;
end.
Можно ли было написать такую же программу, не используя объекты? Конечно, да. И она получилась бы короче, чем наш объектный вариант (с учётом описания классов). В чём же преимущества ООП?
Мы уже отмечали, что ООП — это средство разработки больших программ, моделирующих работу сложных систем. В этом случае очень важно, что при использовании объектного подхода:
• основная программа, описывающая решение задачи в целом, получается простой и понятной; все команды напоминают действия в реальном мире («машина № 2, вперёд!»);
• разработку отдельных классов объектов можно поручить разным программистам, при этом каждый может работать независимо от других;
• если объекты классов Дорога и Машина понадобятся в других разработках, можно будет легко использовать уже готовые классы.
Вопросы и задания
1. Что такое поле в описании класса объекта?
2. Как объявляется класс объектов в программе?
3. Как объявляется переменная для работы с объектом некоторого класса? Что в ней хранится?
4. Как в памяти создаётся экземпляр класса (объект)?
5. Что такое конструктор?
6. Что такое точечная нотация? Как она используется при работе с объектами?
7. Как можно задать начальные значения для полей объекта?
8. Почему в методе ТСаг.move (пример, разобранный в параграфе) не объявлены переменные X и V?
9. Сравните преимущества и недостатки решения рассмотренной задачи «классическим» способом и с помощью ООП. Сделайте выводы.
Подготовьте сообщение
а) «Классы в языке Си»
б) «Классы в языке Javascript»
в) «Классы в языке Python»