14.2. Понятие объекта
Основным понятием ООП и элементом программы является объект, сочетающий в себе как совокупность данных, так и действий над ними. Тип-объект в Turbo Pascal напоминает тип-запись, однако вместо зарезервированного слова record используется слово object, а кроме полей, представляющих данные, в нем перечислены и заголовки подпрограмм, называемых методами. При задании такого типа после зарезервированного слова object перечисляются все поля объекта и заголовки методов, после чего пишется слово end. Так, в рассматриваемом примере используется тип tConnection (связь элементов):
type
tConnection = object
PredElem: Pointer;
NextElem: Pointer;
procedure PutPredElemCPredEl: Pointer);
procedure PutNextElem(NextEl: Pointer);
function GetPredElem: Pointer;
function GetNextElem: Pointer
end;
В этом типе PredElem и NextElem - указатели на предыдущий и последующий элементы в структуре (если соответствующего элемента нет, указатель имеет значение nil). Используются указатели типа Pointer, т. к. элементы могут быть различными: они могут быть и элементами строки, и строками. Далее идут заголовки двух процедур и двух функций, позволяющих либо задавать, либо получать значения указателей объекта.
Естественно, что затем все используемые методы должны быть описаны также, как это делается для подпрограмм в модулях. При этом допускается записывать сокращенный заголовок метода, однако перед ним следует через точку записать имя типа-объекта, к которому относится данная подпрограмма:
procedure tConnection.PutPredElem;
begin
PredElem:=PredEl
end;
Это нужно потому, что несколько разных методов, относящихся к разным объектам, могут иметь одно и то же имя, как и поля в разных типах-записях.
Некоторые объекты программы, особенно находящиеся в начале иерархического дерева, могут и не соответствовать каким-либо реальным объектам. Так, например, объекты типов tConnection (связь), tStructure (структура) и tOperation (операция) не имеют какого либо физического воплощения - они указывают лишь на некоторые свойства других, реальных объектов, таких, как строки, элементы строк. Однако выделение этих общих свойств в отдельные объекты бывает удобно, т. к. позволяет затем не повторять их многократно при описании уже реальных объектов. Такие объекты называются абстрактными, и переменных таких типов в программе, как правило, не бывает.
14.2.1. Инкапсуляция
Под термином "инкапсуляция" понимается совмещение в одном объекте как параметров, так и действий над ними. При этом включенные в объект подпрограммы (методы), как правило, оперируют с данными этого объекта или обращаются к методам объектов-предков (см. п. 14.2.2). Это позволяет объединить в одном месте все свойства объекта, что облегчает понимание работы программы, 1С отладку, модификацию. Так, например, все свойства связей между элементами в структуре текста сосредоточены в типе tConnection. Как правило, к данным Объекта извне непосредственно не обращаются, хотя это и возможно. Для обращения к данным обычно используют соответствующие методы. Так, в рассматриваемом примере для этой цели служат четыре метода PutPredElem, PutNextElem, (idPredElem и GetNextElem, с помощью которых можно задавать и получать значения указателей на предыдущий и последующий элемент. Это обстоятельство не является надуманным. В нашей повседневной жизни так обычно и происходит - мы используем те или иные параметры опосредованно. Если взять уже упоминавшийся нами пример с компьютером, то у него есть такой параметр, как размер свободной памяти на жестком диске. Однако вряд ли владелец компьютера для определения этого параметра будет непосредственно отсчитывать байты - для этой цели служат специальные подпрограммы.
Такое опосредованное обращение к данным позволяет избежать во многих случаях непредвиденных нежелательных изменений параметров. В Turbo Pascal с этой целью используется специальное зарезервированное слово private (приватный), в принципе запрещающее непосредственное обращение к тем или иным данным и методам объекта вне модуля, в котором описан объект. В версии 7.0 приватная секция может размещаться в любом месте объекта (раньше - только в конце после обычных, доступных параметров и методов). Так, если необходимо запретить из основной программы обращаться к данным объекта типа tConniection (напомним, что основная программа редактора находится в отдельном файле), этот тип можно описать следующим образом:
tуре
tConnection = object
procedure PutPredElem(PredEl: Pointer);
procedure PutNextElem(NextEl: Pointer);
function GetPredElem: Pointer;
function GetNextElem: Pointer private
PredElem: Pointer;
NextElem: Pointer;
end;
Если приватная секция находится не в конце объекта, то для ограничения диапазона действия зарезервированного слова private следует после приватной секции поместить зарезервированное слово public (доступный извне) - только в версии 7.0:
type
tConnection = object
private
PredElem: Pointer;
NextElem: Pointer;
public
procedure PutPredElem(PredEl: Pointer);
procedure PutNextElem(NextEl: Pointer);
function GetPredElem: Pointer;
function GetNextElem: Pointer
end;
14.2.2. Наследование
Если из рассматриваемого примера взять тип tStructure (структура), то структуру текста можно задать ее начальным и конечным элементами и связями между отдельными элементами структуры. Связи между отдельными элементами задаются типом tConnection, и было бы нецелесообразно при создании нового типа tStructure заново задавать эти связи. Чтобы этого избежать, в ООП заложено свойство наследования характеристик одного объекта другим. Для этого один из объектов объявляется потомком другого, который, в свою очередь, становится предком этого нового объекта. Потомок наследует все параметры своего предка и его методы, поэтому вторично их описывать нет необходимости, а использовать можно. Это существенно упрощает запись схожих объектов, если установить между ними наследственную связь.
В примере редактора используемые объекты образуют наследственную структуру, которая изображена на рис. 2. В частности, когда задается тип tStructure, его можно объявить потомком типа tConnection (для этого следует после зарезервированного слова object в круглых скобках указать имя типа-предка):
type
tStructure = object(tCornection)
{tStructure - потомок tConnection}
FirstElem: Pointer;
LastElem: Pointer;
constructor Init;
procedure PutFirstElem(FirstEl: Pointer);
procedure PutLastElem(FirstEl: Pointer);
function GetFirstElem: Pointer;
function GetLastElem: Pointer;
procedure InitElem(var NewPoint: Pointer); virtual;
procedure DispElem(PointDel: Pointer); virtual;
procedure PutConnection(FirstPoint, SecondPoint: Pointer);
procedure NewEl(PointPredEl ,PointNextEl: Pointer);
procedure DelEKPointDel: Pointer);
end;
В этом типе имеются собственные данные: FirstElem (указатель на первый Элемент структуры), LastElem (указатель на последний элемент структуры) и методы: Init (инициализация структуры), PutFirstElem (задание значения указателя на первый элемент), PutLastElem (задание значения указателя на последний элемент, GetFirstElem (получение значения указателя на первый элемент), (JetLastElem (получение значения указателя на последний элемент), InitElem (инициализация - создание нового элемента), DispElem (удаление элемента), PutConnection (задание связей между двумя элементами), NewEl (помещение в структуру нового элемента), DelEl (удаление из структуры элемента). Кроме них этот тип наследует от предка tConnection его данные PredElem и NextElem и методы PutPredElem, PutNextElem, GetPredElem и GetNextElem.
В Turbo Pascal непосредственный предок может быть только один. Однако он, в свою очередь, может быть потомком другого типа и т. д. В этом случае потомок наследует характеристики всех своих предков. Так, например, тип tLine (строка) является потомком типа tStructure, который, в свою очередь, является потомком типа tConnection. В связи с этим объект типа tLine может использовать параметры и методы в том числе и типа tConnection.
14.2.3. Полиморфизм
В рассматриваемом примере может возникнут необходимость помещения того или иного символа в элемент строки, строку или весь текст. Для этого можно включить соответствующие методы в типы tElLine (элемент строки), tLine (строка) и tText (текст). Естественно, что эти действия будут отличаться в зависимости от того, куда помещается символ. Если символ помещается в элемент строки, то необходимо знать только номер позиции, куда следует поместить символ. Если символ помещается в строку, то сначала, исходя их координаты X в строке, следует определить, в какой конкретно элемент строки (получить указатель на этот элемент) и в какую позицию в этом элементе необходимо поместить символ. Затем уже можно размещать символ в соответствующем элементе. Если же символ следует поместить в текст, то сначала по координате Y следует определить строку (получить указатель на эту строку), а затем уже выполнить все действия, связанные с размещением символа в строке. Таким образом, следует иметь три разные подпрограммы для трех различных типов. Т. к. все они выполняют в конце концов одно и то же действие - размещают символ в соответствующем месте, было бы заманчиво все их назвать одним именем. В языке Паскаль это делать запрещено - все подпрограммы должны иметь уникальные имена. Для ООП в Turbo Pascal сделано исключение - все эти подпрограммы могут иметь одно и то же имя.
В этой возможности - иметь несколько подпрограмм с одним и тем же именем - и заключается полиморфизм ООП. Вопрос, какая же конкретно подпрограмма будет использоваться в том или ином случае, определяется типом конкретного объекта, использующего эту подпрограмму. Так, если объект типа tText, то будет использована подпрограмма, размещающая символ в тексте, если типа tLine, то подпрограмма, размещающая символ в строке, и т. д.
Если обратиться к рассматриваемому примеру, то у всех трех типов, о которых идет речь, имеется метод с именем PutSymb, выполняющий те действия, о которых говорилось выше. Можно заметить, что список формальных параметров у этих методов различен, что вполне допустимо. Это, в частности, позволяет создавать подпрограммы с изменяющимся набором формальных параметров, что недопустимо в стандарте языка Паскаль. В рассматриваемом примере можно наблюдать и другие подпрограммы с одинаковыми именами.
|