Инкапсуляция
Инкапсуляция есть объединение в единое целое данных и алгоритмов обработки этих данных. В рамках ООП данные называются полями объекта, а алгоритмы - объектными методами.
Инкапсуляция позволяет в максимальной степени изолировать объект от внешнего окружения. Она существенно повышает надежность разрабатываемых программ, т.к. локализованные в объекте алгоритмы обмениваются с программой сравнительно небольшими объемами данных, причем количество и тип этих данных обычно тщательно контролируются. В результате замена или модификация алгоритмов и данных, инкапсулированных в объект, как правило, не влечет за собой плохо прослеживаемых последствий для программы в целом (в целях повышения защищенности программ в ООП почти не используются глобальные переменные).
Другим немаловажным следствием инкапсуляции является легкость обмена
объектами, переноса их из одной программы в другую. Можно сказать, что ООП
Наследование
Наследование есть свойство объектов порождать своих потомков. Объект-потомок автоматически наследует от родителя все поля и методы, может дополнять объекты новыми полями и заменять (перекрывать) методы родителя или дополнять их.
Принцип наследования решает проблему модификации свойств объекта и придает ООП в целом исключительную гибкость. При работе с объектами программист обычно подбирает объект, наиболее близкий по своим свойствам для решения конкретной задачи, и создает одного или нескольких потомков от него, которые «умеют» делать то, что не реализовано в родителе.
Последовательное проведение в жизнь принципа «наследуй и изменяй» хорошо согласуется с поэтапным подходом к разработке крупных программных проектов и во многом стимулирует такой подход.
Полиморфизм
Полиморфизм - это свойство родственных объектов (т.е. объектов, имеющих одного общего родителя) решать схожие по смыслу проблемы разными способами. В рамках ООП поведенческие свойства объекта определяются набором входящих в него методов. Изменяя алгоритм того или иного метода в потомках объекта, программист может придавать этим потомкам отсутствующие у родителя специфические свойства. Для изменения метода необходимо перекрыть его в потомке, т.е. объявить в потомке одноименный метод и реализовать в нем нужные действия. В результате в объекте-родителе и объекте-потомке будут действовать два одноименных метода, имеющие разную алгоритмическую основу и, следовательно, придающие объектам разные свойства. Это и называется полиморфизмом объектов.
В Турбо Паскале полиморфизм достигается не только описанным выше механизмом наследования и перекрытия методов родителя, но и их виртуализацией (см. ниже), позволяющей родительским методам обращаться к методам потомков.
Требуется разработать программу, которая создает на экране ряд графических изображений (точки, окружность, линия, квадрат) и может перемещать эти изображения по экрану. Вид создаваемого программой экрана показан на рис. 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);
Назовем объект-программу именем 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), весьма небольшая плата за мощный инструмент создания современного программного обеспечения.