Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Языки программирования. Концепции и принципы
.pdf
Объектно/ориентированное программирование
PROCEDURE Сети.Удалить(X : Узел);
VAR i; ЧислоСвязей;
PROCEDURE Переписать(ВУзле: Узел; После: ЧислоСвязей)
VAR j: ЧислоСвязей;
BEGIN
j := После;
WITH С[ВУзле].Связан DO
WHILE J < Число-1 DO
Óçëû[j] :=Óçëû[j+1];
j:=j+1;
END;
END;
PROCEDURE Чистить(Связь, ВУзле : Узел);
VAR i : ЧислоСвязей;
BEGIN
i:=l;
WITH С[ВУзле].Связан DO
REPEAT
IF Узлы[i] = Связь THEN
BEGIN
Переписать(ВУзле, i);
Число := Число-1;
EXIT;
END; i:=i+l;
UNTIL i < Число+1
END;
BEGIN
С[X].Включен := FALSE; i := 1;
WITH С[X].Связан DO
BEGIN
REPEAT
Чистить(X, Узлы[j]);
i := i+1;
UNTIL i < Число+1;
Число := 0;
END;
END;
END
421
PROGRAM Клиент; (* головная программа *)
USES УправлениеСетями;
VAR Сеть1, Сеть2 : Сети; (* объявление экземпляров объектного типа *)
BEGIN
Сеть1.Инициализировать; (* работа программы – это работа объектов *)
Сеть2.Инициализировать;
Сеть1.Вставить(33, 13);
Сеть2.Присвоить(Сеть1); (* объект как параметр для другого объекта *)
END Клиент;

422
UNIT УправлениеСетямиСВесом;
INTERFACE
USES УправлениеСетями, ПараметрыСети;
TYPE
Вес = INTEGER;
СетиСВесом = OBJECT(Сети) (* обогащение аналогично Оберону *)
A: ARRAY [1..МаксУзлов] OF BOOLEAN; (* для процедуры ВесПути *)
В: ARRAY [1..МаксУзлов] OF Вес;
PROCEDURE Вставить(X: Узел; Р: Вес);
PROCEDURE Присвоить(VAR Сеть : Сети);
FUNCTION ВесПути(X,Y: Узел): Вес;
END;
END;
IMPLEMENTATION
PROCEDURE СетиСВесом.Вставить(X : Узел; Р: Вес);
BEGIN (* в отличие от Оберона правила видимости *)
С.Вставить(X); (* обеспечивают краткость и защиту других объектов *)
END;
PROCEDURE СетиСВесом.Присвоить (VAR Сеть: Сети);
BEGIN
Сети.Присвоить(Сеть); (* обращение из объекта типа СетиСВесом
к операции, находящейся в его подобъекте родительского типа Сети *)
À := Ñåòü.À;
 := Ñåòü.Â;
END;
(* Реализация следующей операции (функции) на Обероне не приводилась. Поэтому ее не
стоит учитывать при сопоставлении удобства программирования на Турбо Паскале 5.5 и
на Обероне. Программа приведена для полноты и в качестве решения данной ранее
задачи. *)
Перспективы языков программирования
FUNCTION СетиСВесом.ВесПути(X,Y: Узел;): Вес;
VAR i : Óçåë;
Ð : Âåñ;
PROCEDURE ÂÏ(X, Y: Óçåë; VAR PR: Âåñ);
VAR j : ЧислоСвязей;
BEGIN PR := -1;
IF Õ = Y THEN PR := B[X]
ELSE IF ЕстьСвязь(X, Y) THEN PR := B[X] + B[Y]
ELSE
BEGIN A[X]:=FALSE;
(* чтобы не зациклиться при поиске пути *)
WITH С[X].Связан DO
FOR j :=1 ТО Число DO
IF А [Узлы[j]] THEN
(* рекурсивный вызов ВП *)
BEGIN ÂÏ(Óçëû[j], Y, PR);
IF PR >= 0 THEN
(* путь найден *)

Объектно/ориентированное программирование
BEGIN PR:=B[X]+PR; EXIT END;
END;
END;
IF not A[Y] and (PR <= 0) THEN
BEGIN
writeln;
write('нет пути между',Х,'и',У);
A[Y] := TRUE;
(* чтобы не повторять сообщений при выходе из рекурсии*)
END;
END;
BEGIN
FOR i:=l TO МаксУзлов DO A[i] := TRUE;
A[Y] := FALSE;
ВП(X,Y,P);
ВесПути := P;
END;
END.
423
Итак, действующими лицами в программе становятся активные объекты с по
лямипроцедурами. Последние принято называть правилами (действий) объек
тов, методами, операциями объектного типа. Мы будем употреблять термин
«операция», или «правило». В Турбо Паскале строгого запрета на доступ извне
к обычным (непроцедурным) полям объектов нет, но все готово к тому, чтобы та
кой запрет ввести.
Естественная инкапсуляция будет полностью обеспечена – поля объектов
нельзя будет испортить (доступ только через операции этого же объекта, созданные
автором рассматриваемого объектного типа). В Турбо Паскале такой стиль про
граммирования рекомендован, но не обязателен. При обогащении доступ к старым
полям в Турбо Паскале допустим, но с точки зрения идеала развиваемости вполне
можно было ограничиться доступом только посредством старых операций.
Приятно видеть, как программа становится компактнее и прозрачнее за счет
расчистки от загромождения лишними параметрамисетями и уточнениями. Ана
логичные возможности управления видимостью действуют не только при опреде
лении, но и при использовании объектных типов. Обратите внимание, как легко
разрешается потенциальный конфликт наименований, – он возможен только
внутри объектов (но тогда и разбираться с ним могут в принципе сами объекты,
хотя в Турбо Паскале действуют и общие правила, связанные с так называемыми
виртуальными операциями).
7.4. Виртуальные операции
Виртуальные операции Турбо Паскаля 5.5 служат примерами абстрактных атри
бутов. Их конкретизация происходит в контексте обогащенного (производного)
типа и состоит в сопоставлении именам операций конкретных операций из этого
контекста, обладающих теми же названиями. Виртуальные операции Турбо Пас

424
Перспективы языков программирования
каля 5.5 не допускают такой глубокой конкретизации, как атрибутыполя, по
скольку могут быть связаны только с типом, а не с конкретными экземплярами
этого типа.
Один из способов реализации виртуальных операций виден из примера в Обе
роне, где операция (например, Присвоить) из контекста, в котором создается
обогащенный объектный тип СетиСВесом, присваивается переменной, объявлен
ной в том контексте, где объявлен исходный объектный тип (Сети). Ясно, что ана
логично можно присваивать сами операции (или указатели на них) конкретным
объектам (экземплярам объектного типа) в любом подходящем контексте.
В Турбо Паскале для аналогичной цели служит Таблица Виртуальных Опера
ций (ТВО), которую строит компилятор для каждого объектного типа с виртуаль
ными операциями. Каждый объект такого типа содержит указатель на ТВО. Так
что программист избавлен от необходимости явно программировать соответству
ющие объявления процедурных переменных и присваивания, а при использова
нии обогащенных объектов старые операции заменяются автоматически на об
новленные операции с теми же именами.
При объявлении объектных типов с виртуальными операциями (а также при
объявлении их обогащений) необходимо объявлять хотя бы одну так называемую
операциюконструктор и хотя бы одну операциюдеструктор. Они выделяются
ключевыми словами constructor и destructor соответственно. Первые предназна
чены для настройки создаваемого объекта на контекст, вторые – для удаления
объекта (в частности, освобождения памяти). Конструкторы и деструкторы сами
могут быть виртуальными. Первой операцией, работающей в объекте, должен
быть его конструктор (один из его конструкторов), последней операцией (при ак
куратном программировании) – деструктор.
Введение в ЯП конструкторов и деструкторов – попытка достичь большей яс
ности программы, упрощения контроля и оптимизации, сохранив высокий уро
вень динамизма в управлении созданием и удалением объектов со стороны про
граммиста.
В качестве примера применения виртуальных операций приведем с краткими
комментариями демонстрационные модули из фирменного руководства по систе
ме Турбо Паскаль 5.5 (ObjectOriented Programming Guide):
unit Points; (* модуль "Точки" *)
interface
uses Graph;
(* модуль, предоставляющий графические операции *)
type
Location = object (* объектный тип "Координата" *)
X,Y : Integer;
procedure Init(InitX, InitY : Integer);
function GetX : Integer; (* функция дайХ *)
function GetY : Integer; (* функция ДайУ *)
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.
425

426
Перспективы языков программирования
Пока ни одной виртуальной операции нет. Так как одни операции используют
другие (например, 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
427

428
DeltaX := 0; DeltaY := 0; { 0 означает отсутствие изменений } GetDelta := True;
repeat (* запрос изменений *)
KeyChar:= ReadKey; { Считывается нажатие клавиши }
{ можно только догадываться, из какого модуля имя ReadKey }
Quit := Тruе;{ Предполагается, что она допустима }
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) { объектный тип "Дуга"}
Start Angle, 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;
429

430
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(l)
end;
{ Все обогащения типа Point содержат виртуальные операции и поэтому перед использованием должны быть инициализированы конструкторами; ниже следует инициализация
объектов ACircle и АnАrс }
ACircle.Init(151, 82, {Начальные координаты центра – 151, 82;}
50); {начальный радиус – 50 точек растра}
AnArc.Init(151, 82, {Начальные координаты центра – 151, 82;}
25, 0, 90); {начальный радиус – 50 точек растра}
{Нач. угол: 0; Кон. угол: 90}
{ Замените АnАrс на ACircle, чтобы перемещать окружности вместо дуг. Нажмите
клавишу «исполнение» (Enter), чтобы прекратить перемещения (и завершить программу) }
AnArc.Drag(5); { Устанавливается шаг перемещения }
(* при нажатии соответствующих клавиш-стрелок дуга (или окружность) перемещаются по
экрану, наглядно демонстрируя "активность" объектов *)
CloseGraph;
RestoreCRTMode;
end.
Перспективы языков программирования
Итак, если бы не виртуальные операции, то не удалось бы программировать
работу с дугами как обогащение работы с окружностями. Виртуальные операции,
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
