Система программирования Turbo Pascal

         

Основные принципы ООП


Объектно-ориентированное программирование основано на «трех китах» - трех важнейших принципах, придающих объектам новые свойства. Этими принципами являются инкапсуляция, наследование и полиморфизм.

Инкапсуляция

Инкапсуляция есть объединение в единое целое данных и алгоритмов обработки этих данных. В рамках ООП данные называются полями объекта, а алгоритмы - объектными методами.

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

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


«провоцирует» разработку библиотек объектов, таких как Turbo Vision.

Наследование

Наследование есть свойство объектов порождать своих потомков. Объект-потомок автоматически наследует от родителя все поля и методы, может дополнять объекты новыми полями и заменять (перекрывать) методы родителя или дополнять их.

Принцип наследования решает проблему модификации свойств объекта и придает ООП в целом исключительную гибкость. При работе с объектами программист обычно подбирает объект, наиболее близкий по своим свойствам для решения конкретной задачи, и создает одного или нескольких потомков от него, которые «умеют» делать то, что не реализовано в родителе.

Последовательное проведение в жизнь принципа «наследуй и изменяй» хорошо согласуется с поэтапным подходом к разработке крупных программных проектов и во многом стимулирует такой подход.

Полиморфизм

Полиморфизм - это свойство родственных объектов (т.е. объектов, имеющих одного общего родителя) решать схожие по смыслу проблемы разными способами. В рамках ООП поведенческие свойства объекта определяются набором входящих в него методов. Изменяя алгоритм того или иного метода в потомках объекта, программист может придавать этим потомкам отсутствующие у родителя специфические свойства. Для изменения метода необходимо перекрыть его в потомке, т.е. объявить в потомке одноименный метод и реализовать в нем нужные действия. В результате в объекте-родителе и объекте-потомке будут действовать два одноименных метода, имеющие разную алгоритмическую основу и, следовательно, придающие объектам разные свойства. Это и называется полиморфизмом объектов.

В Турбо Паскале полиморфизм достигается не только описанным выше механизмом наследования и перекрытия методов родителя, но и их виртуализацией (см. ниже), позволяющей родительским методам обращаться к методам потомков.


Постановка учебной задачи


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

Требуется разработать программу, которая создает на экране ряд графических изображений (точки, окружность, линия, квадрат) и может перемещать эти изображения по экрану. Вид создаваемого программой экрана показан на рис. 10.1.

Рис. 10.1. Экран, создаваемый учебной программой

Для перемещения изображений в программе будут использоваться клавиши управления курсором, клавиши Ноте, End, PgUp, PgDn (для перемещения по диагональным направлениям) и клавиша Tab для выбора перемещаемого объекта. Выход из программы - клавиша Esc.

Техническая реализация программы потребует использования средств двух стандартных библиотек - CRT и GRAPH, которые еще не рассматривались в этой книге. Чтобы не отвлекать Ваше внимание от основных проблем ООП, при описании реализации учебной задачи особенности использования средств этих библиотек лишь очень кратко комментируются в текстах программы. Если Вы не привыкли «принимать на веру» предлагаемые программные решения и хотите разобраться с деталями вызова незнакомых Вам процедур и функций, рекомендую просмотреть материал гл.13 и гл.14, где описаны эти библиотеки (они не используют средств ООП и, следовательно, могут изучаться до чтения настоящей главы).


Создание объектов


В Турбо Паскале для создания объектов используются три зарезервированных слова: object, constructor, destructor к три стандартные директивы: private, public и virtual.

Зарезервированное слово object используется для описания объекта. Описание объекта должно помещаться в разделе описания типов:

type

MyObject = object

(Поля объекта}

{Методы объекта} 

end ;

Если объект порождается от какого-либо родителя, имя родителя указывается в круглых скобках сразу за словом object:

type

MyDescendantObject = object(MyObject)

.

.

end;

Любой объект может иметь сколько угодно потомков, но только одного родителя, что позволяет создавать иерархические деревья наследования объектов.

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

type

TGraphObj = object

Private {Поля объекта будут скрыты от пользователя}

X,Y: Integer; {Координаты реперной точки}

Color: Word; {Цвет фигуры} 

Public {Методы объекта будут доступны пользователю}

Constructor Init(aX,aY: Integer; aColor: Word);

{Создает экземпляр объекта}

Procedure Draw(aColor: Word); Virtual;

{Вычерчивает объект заданным цветом aColor}

Procedure Show;

{Показывает объект - вычерчивает его цветом Color}

Procedure Hide;

{Прячет объект - вычерчивает его цветом фона} 

Procedure MoveTo(dX,dY: Integer);

{Перемещает объект в точку с координатами X+dX и Y+dY}

end; {Конец описания объекта TGraphObj}

В дальнейшем предполагается создать объекты-потомки от TGraphObj, реализующие все специфические свойства точки, линии, окружности и прямоугольника. Каждый из этих графических объектов будет характеризоваться положением на экране (поля X и Y) и цветом (поле Color). С помощью метода Draw он будет способен отображать себя на экране, а с помощью свойств «показать себя» (метод Show) и «спрятать себя» (метод Hide) сможет перемещаться по экрану (метод MoveTo). Учитывая общность свойств графических объектов, мы объявляем абстрактный объект TGraphObj, который не связан с конкретной графической фигурой. Он объединяет в себе все общие поля и методы реальных фигур и будет служить родителем для других объектов.

Директива Private в описании объекта открывает секцию описания скрытых полей и методов. Перечисленные в этой секции элементы объекта «не видны» программисту, если этот объект он получил в рамках библиотечного ТР(/-модуля. Скрываются обычно те поля и методы, к которым программист (в его же интересах!) не должен иметь непосредственного доступа. В нашем примере он не может произвольно менять координаты реперной точки (X.Y), т.к. это не приведет к перемещению объекта. Для изменения полей X и Y предусмотрены входящие в состав объекта методы Init и MoveTo. Скрытые поля и методы доступны в рамках той программной единицы (программы или модуля), где описан соответствующий объект. В дальнейшем предполагается, что программа будет использовать модуль GraphObj с описанием объектов. Скрытые поля будут доступны в модуле GraphObj, но недоступны в использующей его основной программе. Разумеется, в рамках реальной задачи создание скрытых элементов объекта вовсе необязательно. Я ввел их в объект TGraphObj лишь для иллюстрации возможностей ООП.

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

Вариант объявления объекта TGraphObj без использования механизма private...public:

type

TGraphObj = object

X,Y: Integer;

Color: Word;

Constructor Init(aX,aY: Integer; aColor: Word);

Procedure Draw(aColor: Word); Virtual;

Procedure Show;

Procedure Hide;

Procedure MoveTo(dX,dY: Integer); 

end;

Описания полей ничем не отличаются от описания обычных переменных. Полями могут быть любые структуры данных, в том числе и другие объекты. Используемые в нашем примере поля X и Y содержат координату реперной (характерной) точки графического объекта, а поле Color - его цвет. Реперная точка характеризует текущее положение графической фигуры на экране и, в принципе, может быть любой ее точкой.

В нашем примере она совпадает с координатами точки в описываемом ниже объекте TPoint, с центром окружности в объекте TCircle, первым концом прямой в объекте TLine и с левым верхним углом прямоугольника в объекте TRect.

Для описания методов в ООП используются традиционные для Паскаля процедуры и функции, а также особый вид процедур - конструкторы и деструкторы. Конструкторы предназначены для создания конкретного экземпляра объекта, ведь объект - это тип данных, т.е. «шаблон», по которому можно создать сколько угодно рабочих экземпляров данных объектного типа (типа TGraphOhj, например). Зарезервированное слово constructor, используемое в заголовке конструктора вместо procedure, предписывает компилятору создать особый код пролога, с помощью которого настраивается так называемая таблица виртуальных методов (см. ниже). Если в объекте нет виртуальных методов, в нем может не быть ни одного конструктора, наоборот, если хотя бы один метод описан как виртуальный (с последующим словом Virtual, см. метод Draw), в состав объекта должен входить хотя бы один конструктор и обращение к конструктору должно предшествовать обращению к любому виртуальному методу.

Типичное действие, реализуемое конструктором, состоит в наполнении объектных полей конкретными значениями. Следует заметить, что разные экземпляры одного и того же объекта отличаются друг от друга только содержимым объектных полей, в то время как каждый из них использует одни и те же объектные методы. В нашем примере конструктор Init объекта TGraphObj получает все необходимые для полного определения экземпляра данные через параметры обращения аХ, аY и aColor.

Процедура Draw предназначена для вычерчивания графического объекта. Эта процедура будет реализовываться в потомках объекта TGraphObj по-разному. Например, для визуализации точки следует вызвать процедуру PutPixel, для вычерчивания линии - процедуру Line и т.д. В объекте TGraphObj процедура Draw определена как виртуальная («воображаемая»). Абстрактный объект TGraphObj не предназначен для вывода на экран, однако наличие процедуры Draw в этом объекте говорит о том, что любой потомок TGraphObj должен иметь собственный метод Draw, с помощью которого он может показать себя на экране.

При трансляции объекта, содержащего виртуальные методы, создается так называемая таблица виртуальных методов (ТВМ), количество элементов которой равно количеству виртуальных методов объекта. В этой таблице будут храниться адреса точек входа в каждый виртуальный метод. В нашем примере ТВМ объекта TGraphObj хранит единственный элемент - адрес метода Draw. Первоначально элементы ТВМ не содержат конкретных адресов. Если бы мы создали экземпляр объекта TGraphObj с помощью вызова его конструктора Init, код пролога конструктора поместил бы в ТВМ нужный адрес родительского метода Draw. Далее мы создадим несколько потомков объекта TGraphObj. Каждый из них будет иметь собственный конструктор, с помощью которого ТВМ каждого потомка настраивается так, чтобы ее единственный элемент содержал адрес нужного метода Draw. Такая процедура называется поздним связыванием объекта. Позднее связывание позволяет методам родителя обращаться к виртуальным методам своих потомков и использовать их для реализации специфичных для потомков действий.

Наличие в объекте TGraphObj виртуального метода Draw позволяет легко реализовать три других метода объекта: чтобы показать объект на экране в методе Show, вызывается Draw с цветом aColor, равным значению поля Color, а чтобы спрятать графический объект, в методе Hide вызывается Draw со значением цвета GetBkColor, т.е. с текущим цветом фона.

Рассмотрим реализацию перемещения объекта. Если потомок TGraphObj (например, TLine) хочет переместить себя на экране, он обращается к родительскому методу MoveTo. В этом методе сначала с помощью Hide объект стирается с экрана, а затем с помощью Show показывается в другом месте. Для реализации своих действий и Hide, и Show обращаются к виртуальному методу Draw. Поскольку вызов MoveTo происходит в рамках объекта TLine, используется ТВМ этого объекта и вызывается его метод Draw, вычерчивающий прямую. Если бы перемешалась окружность, ТВМ содержала бы адрес метода Draw объекта TCircle и визуализация-стирание объекта осуществлялась бы с помощью этого метода.

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

type 

TGraphObj = object

...

end;

Constructor TGraphObj.Init;

begin

X := aX;

Y := aY; Color := aColor 

end;

Procedure TGraphObj-Draw; 

begin

{Эта процедура в родительском объекте ничего не делает, поэтому экземпляры TGraphObj не способны отображать себя на экране. Чтобы потомки объекта TGraphObj были способны отображать себя, они должны перекрывать этот метод} 

end;

Procedure TGraphObj.Show; 

begin

Draw(Color) 

end;

Procedure TGraphObj.Hide; 

begin

Draw(GetBkColor) 

end;

Procedure TGraphObj.MoveTo; 

begin

Hide;

X := X+dX;

Y := Y+dY;

Show 

end;

Отмечу два обстоятельства. Во-первых, при описании методов имя метода дополняется спереди именем объекта, т.е. используется составное имя метода. Это необходимо по той простой причине, что в иерархии родственных объектов любой из методов может быть перекрыт в потомках. Составные имена четко указывают принадлежность конкретной процедуры. Во-вторых, в любом объектном методе можно использовать инкапсулированные поля объекта почти так, как если бы они были определены в качестве глобальных переменных. Например, в конструкторе TGraph.Init переменные в левых частях операторов присваивания представляют собой объектные поля и не должны заново описываться в процедуре. Более того, описание

Constructor TGraphObj.Init; 

var

X,Y: Integer; {Ошибка!} 

Color: Word; {Ошибка!} 

begin

end;

вызовет сообщение о двойном определении переменных X, Y и Color (в этом и состоит отличие в использовании полей от глобальных переменных: глобальные переменные можно переопределять в процедурах, в то время как объектные поля переопределять нельзя).

Обратите внимание: абстрактный объект TGraphObj не предназначен для вывода на экран, поэтому его метод Draw ничего не делает. Однако методы Hide, Show и MoveTo «знают» формат вызова этого метода и реализуют необходимые действия, обращаясь к реальным методам Draw своих будущих потомков через соответствующие ТВМ. Это и есть полиморфизм объектов.

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

type

TPoint = object(TGraphObj)

Procedure Draw(aColor); Virtual; 

end;

Procedure TPoint.Draw; 

begin

PutPixel(X,Y,Color) {Показываем цветом Color пиксель с координатами X и Y} 

end;

В новом объекте TPoint можно использовать любые методы объекта-родителя TGraphObj. Например, вызвать метод MoveTo, чтобы переместить изображение точки на новое место. В этом случае родительский метод TGraphObj.MoveTo будет обращаться к методу TPoint.Draw, чтобы спрятать и затем показать изображение точки. Такой вызов станет доступен после обращения к конструктору Init объекта TPoint, который нужным образом настроит ТВМ объекта. Если вызвать TPoint.Draw до вызова Init, его ТВМ не будет содержать правильного адреса и программа «зависнет».

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

type

TLine = object(TGraphObj)

dX,dY: Integer; {Приращения координат второго конца} 

Constructor Init(X1,Y1,X2,Y2: Integer; aColor: Word); 

Procedure Draw(aColor: Word); Virtual;

end; ,

Constructor TLine.Init;

{Вызывает унаследованный конструктор TGraphObj для инициации полей X, Y и Color. Затем инициирует поля dX и dY} 

begin

{Вызываем унаследованный конструктор}

Inherited Init(XI,Yl,aColor);

{Инициируем поля dX и dY}

dX := Х2-Х1;

dY := Y2-Y1 

end;

Procedure Draw; 

begin

SetColor(Color);{Устанавливаем цвет Color} 

Line(X,Y,X+dX,Y+dY){Вычерчиваем линию}

end;

В конструкторе TLine.Init для инициации полей X, Y и Color, унаследованных от родительского объекта, вызывается унаследованный конструктор TGraph.Init, для чего используется зарезервированное слово inherited (англ.- унаследованный):

Inherited Init(XI,Yl,aColor) ;

С таким же успехом мы могли бы использовать и составное имя метода:

TGraphObj.Init(Xl,Yl,aColor);

Для инициации полей dX и dY вычисляется расстояние в пикселах по горизонтали и вертикали от первого конца прямой до ее второго конца. Это позволяет в методе TLine.Draw вычислить координаты второго конца по координатам первого и смещениям dX и dY. В результате простое изменение координат реперной точки X, Y в родительском методе TGraph.MoveTo перемещает всю фигуру по экрану.

Теперь нетрудно реализовать объект TCircle для создания и перемещения окружности:

type

TCircle = object(TGraphObj)

R: Integer; {Радиус}

Constructor Init(aX,aY,aR: Integer;

Procedure Draw(aColor: Virtual); 

end ;

Constructor TCircle.Init; 

begin

Inherited Init(aX,aY,aColor);

R := aR 

end ;

aColor: Word)

Procedure TCircle.Draw; 

begin

SetColor(aColor); {Устанавливаем цвет Color} 

Circle(X,Y,R) {Вычерчиваем окружность}

end;

В объекте TRect, с помощью которого создается и перемещается прямоугольник, учтем то обстоятельство, что для задания прямоугольника требуется указать четыре целочисленных параметра, т.е. столько же, сколько для задания линии. Поэтому объект TRect удобнее породить не от TGraphObj, а от TLine, чтобы использовать его конструктор Init:

type

TRect = object(TLine)

Procedure Draw(aColor: Word); 

end;

Procedure TRect.Draw; 

begin

SetColor(aColor);

Rectangle(X,Y,X+dX,Y+dY) {Вычерчиваем прямоугольник} 

end;

Чтобы описания графических объектов не мешали созданию основной программы, оформим эти описания в отдельном модуле GraphObj:

Unit GraphObj; Interface

{Интерфейсная часть модуля содержит только объявления объектов}

type

TGraphObj = object

...

end;

TPoint = object(TGraphObj)

...

end;

TLine = object(TGraphObj)

...

end;

TCircle = object(TGraphObj)

end;

TRect = object(TLine)

...

end; 

Implementation

{Исполняемая часть содержит описания всех объектных методов}

Uses Graph;

Constructor TGraphObj.Init;

...

end.

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

Constructor TGraphObj.Init; 

или таким:

Constructor TGraphObj.Init(aX,aY: Integer; aColor: Word);


Использование объектов


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

Назовем объект-программу именем TGraphApp и разместим его в модуле GraphApp (пока не обращайте внимание на точки, скрывающие содержательную часть модуля -позднее будет представлен его полный текст):

Unit GraphApp;

Interface

type

TGraphApp = object

Procedure Init;

Procedure Run;

Destructor Done; 

end;

Implementation Procedure TGraphApp.Init;

...

end;

...

end. 

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

Program Graph_0bjects;

Uses GraphApp;

var

App: TGraphApp; 

begin

App.Init;

App.Run;

App.Done 

end.

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

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

var

Арр: TGraphApp;

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

App.Init; 

Арр.Run; 

Арр.Done;

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

Program Graph_0bjects;

Uses GraphApp;

type

PGraphApp = TGraphApp; 

var

App: PGraphApp; 

begin

App := New(PGraphApp,Init)

Арр.Run;

Арр.Done 

end;

Для инициации динамической переменной Арр используется вызов функции 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; 

Procedure 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, '\tp\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 ; { TGraphApp .Init}

{-----------}

Procedure TGraphApp .Run ;

{Выбирает объект с помощью Tab и перемещает его по экрану}

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 := 3 

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; {TGraphApp. Run}

{-----------}

Destructor TGraphApp . Done ;

{Закрывает графический режим} 

begin

CloseGraph 

end; {TGraphApp. Done}

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

В заключении следует сказать, что формалистика ООП в рамках реализации этой технологии в Турбо Паскале предельно проста и лаконична. Согласитесь, что введение лишь шести зарезервированных слов, из которых действительно необходимыми являются три (object, constructor и virtual), весьма небольшая плата за мощный инструмент создания современного программного обеспечения.