В.Ш. Кауфман - Языки программирования - концепции и принципы (1990) (1160787), страница 76
Текст из файла (страница 76)
использовании обогащенных объектов старые операции на обновленные с теми же
именами заменяются автоматически.
При объявлении объектных типов с виртуальными операциями (а также при
объявлении их обогащений) необходимо объявлять хотя бы одну так называемую
операцию-констуктор и хотя бы одну операцию-деструктор Они выделяются
ключевыми словами constructor и destructor соответственно. Первые
предназначены для настройки создаваемого объекта на контекст, вторые - для
удаления объекта (в частности, освобождения памяти). Конструкторы и
деструкторы сами могут быть виртуальными. Первой операцией, работающей в
объекте, должен быть его конструктор (один из его конструкторов), последней
операцией (при аккуратном программировании) - деструктор. Введение в ЯП
конструкторов и деструкторов - попытка достичь большей ясности программы,
упрощения контроля и оптимизации, сохранив высокий уровень динамизма в
управлении созданием и удалением объектов со стороны программиста.
В качестве примера применения виртуальных операций приведем с краткими
комментариями демонстрационные модули из фирменного руководства по системе
Турбо Паскаль 5.5 (Object-Oriented Programming Guide).
unit Points; (* модуль "Точки" *)
interface
uses Graph; (* модуль, предоставляющий графические операции *)
type
Location = object (* объектный тип "Координата" *)
X,Y : Integer;
procedure Init(InitX, InitY : Integer);
function GetX : Integer; (* функция ДайX *)
function GetY : Integer; (* функция ДайY *)
end;
Point = object (Location) (* объектный тип "Точка" *)
Visible : Boolean; (* Видимо *)
procedure Init(InitX, InitY : Integer);
procedure Show; (* Показать *)
procedure Hide; (* Скрыть *)
function IsVisible : Boolean; (* Видимо? *)
procedure MoveTo(NewX, NewY : Integer); (* Переместить *)
end;
implementation
{--------------------------------------------------------}
{ Реализация операций типа Location: }
{--------------------------------------------------------}
procedure Location.Init(InitX, InitY : Integer);
begin
X := InitX;
Y := InitY;
end;
function Location.GetX : Integer;
begin
GetX := X;
end;
function Location.GetY : Integer;
begin
GetY := Y;
end;
{--------------------------------------------------------}
{ Реализация операций типа Points: }
{--------------------------------------------------------}
procedure Point.Init(InitX, InitY : Integer);
begin
Location.Init(InitX, InitY);
Visible := False;
end;
procedure Point.Show;
begin
Visible := True;
PutPixel(X, Y, GetColor); (* услуги модуля Graph - нарисовать *)
end; (* точку указанного цвета *)
procedure Point.Hide;
begin
Visible := False;
PutPixel(X, Y, GetBkColor); (* услуги модуля Graph - нарисовать *)
end; (* точку фонового цвета *)
function Point.IsVisible : Boolean;
begin
IsVisible := Visible;
end;
procedure Point.MoveTo(NewX, NewY : Integer);
begin
Hide; (* параметры не нужны! Сама точка - активный объект *)
Location.Init(NewX, NewY);
Show;
end;
end.
Пока ни одной виртуальной операции нет. Так как одни операции
используют другие (например, Point.Init или Point.MoveTo используют операции
типа Location), то они будут использовать именно эти старые операции даже
тогда, когда будут введены обогащения типов Location и Point. Если это
нежелательно, типы Location и Point нужно программировать так, как показано
в следующем модуле, снабжая подлежащие последующей замене операции признаком
virtual.
unit Figures; (* модуль Фигуры *)
interface
uses Graph, Crt; (* еще один вспомогательный модуль *)
(* разбираться в том, из какого именно модуля импортированы имена, в Турбо
Паскале неприятно - оцените решение из Оберона! Полезно сопоставить и с
Модулой-2. Правда, квалифицированный программист станет систематически
применять комментарии, если ЯП его не заставляет сообщать потенциальному
читателю столь необходимую информацию. *)
type
Location = object
X,Y : Integer;
procedure Init(InitX, InitY : Integer);
function GetX : Integer;
function GetY : Integer;
end;
PointPtr = ^Point; (* "^" - знак указателя *)
Point = object (Location)
Visible : Boolean;
constructor Init(InitX, InitY : Integer);
destructor Done; virtual;
procedure Show; virtual;
procedure Hide; virtual;
function IsVisible : Boolean;
procedure MoveTo(NewX, NewY : Integer);
procedure Drag(DragBy : Integer); virtual;
(* задает относительный шаг при перемещении фигуры по экрану *)
end;
CirclePtr = ^Circle;
Circle = object (Point) (* объектный тип "Окружность" *)
Radius : Integer;
constructor Init(InitX, InitY : Integer;
InitRadius : Integer);
procedure Show; virtual; (* показать *)
procedure Hide; virtual; (* скрыть *)
procedure Expand(ExpandBy: Integer); virtual; (* увеличить *)
procedure Contract(ContractBy: Integer); virtual;(* уменьшить *)
end;
implementation
{--------------------------------------------------------}
{ Реализация операций типа Location: }
{--------------------------------------------------------}
procedure Location.Init(InitX, InitY : Integer);
begin
X := InitX;
Y := InitY;
end;
function Location.GetX : Integer;
begin
GetX := X;
end;
function Location.GetY : Integer;
begin
GetY := Y;
end;
{--------------------------------------------------------}
{ Реализация операций типа Points: }
{--------------------------------------------------------}
constructor Point.Init(InitX, InitY : Integer);
begin
Location.Init(InitX, InitY);
Visible := False;
end;
destructor Point.Done;
begin
Hide;
end;
procedure Point.Show;
begin
Visible := True;
PutPixel(X, Y, GetColor);
end;
procedure Point.Hide;
begin
Visible := False;
PutPixel(X, Y, GetBkColor);
end;
function Point.IsVisible : Boolean;
begin
IsVisible := Visible;
end;
procedure Point.MoveTo(NewX, NewY : Integer);
begin
Hide;
X := NewX;
Y := NewY;
Show;
end;
(* пока все - как было; ниже - обеспечивается движение фигуры по экрану; все
начинается со вспомогательной функции, проверяющей наличие изменений
координат *)
function GetDelta(var DeltaX : Integer;
var DeltaY : Integer) : Boolean;
var
KeyChar : Char;
Quit : Boolean;
begin
DeltaX := 0; DeltaY := 0; { 0 означает отсутствие изменений }
GetDelta := True;
repeat (* запрос изменений *)
KeyChar := ReadKey; { Считывается нажатие клавиши }
(* можно только догадываться, из какого модуля имя ReadKey *)
Quit := True; { Предполагается, что она допустима }
case Ord(KeyChar) of
0: begin { 0 - расширенный двухбайтный код }
KeyChar := ReadKey; { Считывается второй байт кода }
case Ord(KeyChar) of
72: DeltaY := -1; { Клавиша Up; уменьшение Y }
80: DeltaY := 1; { Клавиша Down; увеличение Y }
75: DeltaX := -1; { Клавиша Left; уменьшение X }
77: DeltaX := 1; { Клавиша Right; увеличение X }
else Quit := False; { Другие коды игнорируются }
end; { case } (* так применяются комментарии *)
end;
13: GetDelta := False; { Клавиша "Исполнение"
означает отсутствие (конец) изменений }
else Quit := False; { Игнорируются другие клавиши }
end; { case }
until Quit;
end;
procedure Point.Drag(DragBy : Integer);
var
DeltaX, DeltaY : Integer;
FigureX, FigureY : Integer;
begin
Show; { Показывается фигура, подлежащая перемещению }
FigureX := GetX;
FigureY := GetY;
{ Цикл собственно перемещения : }
while GetDelta(DeltaX, DeltaY) do
begin
FigureX := FigureX + (DeltaX * DragBy);
FigureY := FigureY + (DeltaY * DragBy);
MoveTo(FigureX, FigureY);
end;
end;
{--------------------------------------------------------}
{ Реализация операций типа Circle: }
{--------------------------------------------------------}
constructor Circle.Init(InitX, InitY : Integer;
InitRadius : Integer);
begin
Point.Init(InitX, InitY);
Radius := InitRadius;
end;
procedure Circle.Show;
begin
Visible := True;
Graph.Circle(X, Y, Radius); (* рисуется окружность *)
end;
procedure Circle.Hide;
var
TempColor : Word;
begin
TempColor := Graph.GetColor;
Graph.SetColor(GetBkColor);
Visible := False;
Graph.Circle(X, Y, Radius);
(* чтобы стереть, рисуется окружность фонового цвета *)
Graph.SetColor(TempColor);
end;
procedure Circle.Expand(ExpandBy : Integer);
begin
Hide;
Radius := Radius + ExpandBy;
if Radius <0 then Radius := 0;
Show;
end;
procedure Circle.Contract(ContractBy : Integer);
begin
Expand(-ContractBy);
end;
{ Раздел инициализации в этом модуле отсутствует }
end.
program FigureDemo; (* Главная программа *)
uses Crt, DOS, Graph, Figures;
type
Arc = object (Circle) (* объектный тип "Дуга" *)
StartAngle, EndAngle : Integer; (* начальный и конечный угол *)
constructor Init(InitX, InitY : Integer;
InitRadius : Integer;
InitStartAngle, InitEndAngle : Integer);
procedure Show; virtual;
(* заменять виртуальные можно только виртуальными *)
procedure Hide; virtual;
end;
var
GraphDriver : Integer;
GraphMode : Integer;
ErrorCode : Integer;
AnArc : Arc;
ACircle : Circle;
{--------------------------------------------------------}
{ Реализация операций типа Arc: }
{--------------------------------------------------------}
constructor Arc.Init(InitX,InitY : Integer;
InitRadius : Integer;
InitStartAngle, InitEndAngle : Integer);
begin
Circle.Init(InitX, InitY, InitRadius);
StartAngle := InitStartAngle;
EndAngle := InitEndAngle;
end;
procedure Arc.Show;
begin
Visible := True;
Graph.Arc(X, Y, StartAngle, EndAngle, Radius);
(* при работе с дугами нельзя пользоваться операциями над полными
окружностями - поэтому применяется виртуальная операция Show (Показать) *)
end;
procedure Arc.Hide;
var
TempColor : Word;
begin
TempColor := Graph.GetColor;
Graph.SetColor(GetBkColor);
Visible := False;
(* вычерчивание дуги в фоновом цвете, чтобы скрыть ее *)
Graph.Arc(X, Y, StartAngle, EndAngle, Radius);
(* при работе с дугами нельзя пользоваться операциями над полными
окружностями - поэтому применяется виртуальная операция Hide (Скрыть) *)
SetColor(TempColor);
end;
{--------------------------------------------------------}
{ Тело главной программы: }
{--------------------------------------------------------}
begin
GraphDriver := Detect; { Используются услуги модуля DOS для определения
типа применяемой клавиатуры }
DetectGraph(GraphDriver, GraphMode);
InitGraph(GraphDriver, GraphMode,'');
if GraphResult <> GrOK then (* можно ли пользоваться графикой? *)
begin
WriteLn('>>Halted on graphics error:',
GraphErrorMsg(GraphDriver));
Halt(1)
end;
{ Все обогащения типа Point содержат виртуальные операции и поэтому перед
использованием должны быть инициализированы конструкторами; ниже следует
инициализация объектов ACircle и AnArc }
ACircle.Init(151, 82, { Начальные координаты центра - 151, 82; }