Методы-функции и методы-процедуры
ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ
Часть 1 КЛАССЫ И ОБЪЕКТЫ
УЧЕБНОЕ ПОСОБИЕ по курсу
"Прикладное программирование"
ВВЕДЕНИЕ
Object-Oriented Programming (Объектно-ориентированное программирование - ООП) стало естественным способом разработки сложного программного обеспечения с графическим интерфейсом пользователя. Оно основано на объектно-ориентированной модели, для которой объекты являются основным предметом рассмотрения. Эта модель включает описание объектов и средства их взаимодействия друг с другом. Объектно-ориентированный подход при разработке программного обеспечения уже продемонстрировал свои возможности в создании систем в самых разных областях любого размера и сложности.
Объект (Object) - это переменная структурированного типа данных, включающая, как элементы данных, так и операции с этими элементами, упакованными вместе для удобства использования. Объединение данных и кода в одну конструкцию называетсяинкапсуляцией (encapsulation). Важно подчеркнуть, что операции и данные, которыми они оперируют, тесно связаны друг с другом. Простое включение операций и данных в одну структуру возможно и в паскалевской записи.
Каждый объект является экземпляром (instance) определенного класса. Во время выполнения программы объекты взаимодействуют друг с другом, вызывая методы-операции, являющиеся подпрограммами, характерными для определенного класса.
Класс (Class) - это структурированный тип данных или объединяющая концепция (абстракция) набора объектов (экземпляров), имеющих общие характеристики. Класс определяет семантику - общий интерфейс с окружающим миром, посредством которого можно взаимодействовать с отдельными объектами. Все представители (экземпляры) одного класса аналогичны друг другу, поскольку они имеют одинаковый интерфейс.
Классы могут быть связаны друг с другом отношенияминаследования (inheritance), с их помощью количество элементов данных и/или операций с ними может увеличиваться, а описания существующих классов многократно использоваться при описании новых. Наследование является одним из механизмов, посредством которого объекты одного класса могут включаться в работу объектов другого класса. Наследование предоставляет мощный механизм моделирования отношений, существующих в реальном мире.
При определенных обстоятельствах объекты могут вызывать методы-операции не только их непосредственного класса, но и любого из классов-предков, таким образом поддерживаетсяполиморфизм (polymorphism). Полиморфизм - это, в переводе с греческого, много форм. Применительно к ООП это означает, что может существовать несколько версий какого-то метода и один и тот же вызов этого метода может использовать различные его версии.
КЛАССЫ И ОБЪЕКТЫ
Данные в объекте определяются и выглядят подобно полям записи (Record) в Паскале, но без вариантной части. Могут быть объявлены поля различных типов, которым при создании объекта или в процессе работы программы присваиваются конкретные значения. Комбинация значений всех полей объекта определяетсостояние этого объекта. Изменение значения поля изменяет и состояние объекта. Поэтомузначения полей уникальны для каждого экземпляра класса (объекта), хотя и может быть несколько объектов с полностью идентичными значениями полей.
Объявляются поля данных внутри описания класса способом, аналогичным определению полей записей и похожим на объявление обычных переменных. Можно перечислить одно или более имен полей одного типа, разделенных запятыми, а затем указать наименование типа, отделив его двоеточием. Заканчивается каждое описание точкой с запятой после названия типа.
Как источник действий, объект использует набор методов, т.е. подпрограмм, разработанных для операций с полями. Все методы совместно определяютповедение объекта. Активирование метода объекта проявляется в действии, совершаемом объектом. Адреса вызова некоторых методов объекта определяются на этапе компиляции программы, а других - с помощью специальной таблицы, создаваемой после инициализации программы. Каждый объект содержит указатель на такую специальную таблицу, содержащую информацию, необходимую для вызова метода. Эта таблица является принадлежностью класса и для нее выделяется память. Таким образом,методы у объектов одного классаобщие.
Классическое правило ООП утверждает, что для обеспечения надежности нежелателен прямой доступ к полям объекта: чтение и изменение их содержимого должно производиться посредством вызова соответствующих методов. В объектах Delphi пользователь может быть полностью отгорожен от полей с помощьюсвойств, в то время как поля у записи не защищены и доступны для возможного неверного использования.
Таким образом, класс - это структурированный тип данных языка Object Pascal (OP). Класс представляет собой описание того, как будет выглядеть и вести себя его представитель (экземпляр или объект).
Во многих объектно-ориентированных языках объявление переменной, имеющей тип класса, автоматически создает объект. В OP вместо этого используется ссылочная модель объектов (object reference model), т.е. каждая переменная типа класса содержит не значение объекта, а лишь ссылку (указатель) на область памяти, в которой содержится объект, т.е. переменная типа класс - это просто ссылка на экземпляр. Таким образом, в OP все экземпляры или объекты могут быть только динамическими, поэтому отпала необходимость в использовании непременного атрибута указателя '^' - caret.
Для того чтобы использовать новый тип данных в программе, необходимо как минимум объявить переменную этого типа. За выделение памяти, т.е. за создание новых своих экземпляров-объектов отвечает класс с помощью метода, называемогоконструктором. Поскольку, как известно, выделение памяти для динамических переменных происходит только во время выполнения программы, то фактически объекты вызываются к жизни во время исполнения. Статически во время трансляции программы выделяется память только под объектную ссылку, размер которой существенно меньше, чем необходимо для хранения экземпляра класса, поскольку память для полей не выделяется.
Отметим, что, объявляя в программе переменную того или иного класса, мы фактически при работе программы лишь создадим структуру, включающую только поля, а методы у всех экземпляров одного класса размещены в одном месте - в описании класса. Доступ к ним осуществляется через указатель на класс, имеющийся у каждого экземпляра.
Таким образом, класс это не просто описание структурированного типа данных, а активная структура, существующая в программе и способная выполнять определенные действия по созданию экземпляров класса. Создание, т.е. определение адреса и выделение памяти, а также инициализация экземпляров класса (объектов) осуществляется с помощью класса.
Однако класс не уничтожает экземпляры, поскольку эту способность поддерживает сам активный экземпляр, т.е. экземпляры самоуничтожаются при вызове соответствующего метода, называемогодеструктором.
а) Объявление класса, т.е. объявление типа данных, определяет состав полей данных экземпляров класса и набор допустимых операций над ними, и его синтаксис следующий:
Type <имя класса>=Сlass[(<имя родительского класса>)}
Определение класса> // Определение полей, свойств и методов
End;
Примечания:
• Классы могут быть объявлены либо в секции интерфейса модуля, либо на верхнем уровне вложенности секции реализации. Таким образом, не допускается описание класса внутри процедур и функций и других блоков кода. Они могут быть только глобальными типами.
• Возможно предварительное объявление класса, но без использования директивы Forward'.
Type <имя класса>=Сlass[(<имя родительского класса>)};
• В OP у всех классов есть общий класс-прародитель TObject. При создании пользовательского класса непосредственно от TObject имя родительского класса можно не указывать.
• Определение класса может быть разделено на несколько разделов с разной степенью видимости (доступности) их компонентов. Каждый раздел может начинаться с одного из ключевых слов-директив:Private, Protected, Public, Published (если компилировался с директивой SM+). По умолчанию, если нет ни одного слова-директивы или до первой директивы. определяющим разделом является Public.
• Объявление класса включает три основных группы компонентов объектов, называемых интерфейсом класса:
• данные объекта или атрибуты исходных данных, называемые чаще всего полями;
' методы объекта или операции над данными;
• свойства объекта - это высокоуровневые атрибуты данных, которые тесно связаны с соответствующими методами доступа к данным.
• Объявление полей в каждом из четырех разделов объявления класса должно предшествовать объявлению методов и свойств.
• Имена классов принято начинать с буквыТ, а имена полей с буквы F.
• По существу имеется два вида полей внутри экземпляра класса: поля прямого доступа и косвенного доступа:
• поле прямого доступа - это реальная часть данных, непосредственно встроенная в экземпляр класса. Примером может служить целое число или строка. Прямой доступ требует ресурса памяти для хранения каждого экземпляра класса, в точности равного объявленному размеру, поэтому значения этих полей могут храниться внутри каждого экземпляра класса. Выделение памяти под строки Delphi делает динамически и автоматически:
• поле косвенного доступа - это ссылка на некоторый другой объект, обычно на экземпляр другого класса. Поле косвенного доступа требует сравнительно немного памяти для хранения ссылки, поскольку в этом поле хранится адрес - 4 байта.
• Объект можетсодержать (включать) другой объект, т.е. владеть им. В этом случае объект-хозяин ответственен как за создание экземпляра этого объекта (выделение памяти соответствующим конструктором, когда это необходимо), так и за уничтожение (соответствующим деструктором, когда он больше не требуется). Например, экранная форма владеет всеми объектами, на ней расположенными: кнопками, полями редактирования и т.п.
• Объект может бытьассоциирован с другим объектом. В этом случае объекты "знают " друг о друге, могут вызывать методы друг друга и ссылаться, но они не отвечают за создание и уничтожение друг друга. Например, совокупность компонентов для работы с базами данных, организации DDE-связи и т.п.
б) Создание экземпляра класса. Для создания экземпляра необходимо объявить переменную в секцииVar, а затем отвести ей память в куче соответствующим конструктором.
в) Обращение к полям экземпляра класса. Можно обращаться к полям данных следующим образом:
• используя точечную нотацию (или полное имя):
<имя объекта>.<имя поля>:=<значение>;
• используя операторWith, как при обращении к полям записи в Паскале:
With <имя объекта>Do Begin
<имя поля ]>:=<значение1>;
<имя поля 2>:=<значение2>;
. . .
End;
Рассмотрим пример объявления, создания и использования объекта.
• Объявим класс TLine:
Type TLine=Class
X, Y, Length, Angle: Integer;// Поля класса
Constructor Create; // Конструктор класса
Procedure MoveTo(NewX, NewY: Integer);// 1-й метод класса
Procedure Rotate(ByAngle: Integer);// 2-й метод класса
End;
• Объявим и создадим экземпляр класса, выделив под него память:
Var ALine: TLine;
Begin
. . .
ALine:=TLine.Create; // На самом деле ALine всего лишь ссылка
. . .
• Обратимся к полям (атрибутам) экземпляра:
. . .
ALine.X:=10;
ALine.Y:=10;
ALine.Length:=100;
ALine.Angle:=30; // Пока все аналогично полям записи
• Объявим новый класс (класс-потомок) от созданного класса:
Type
TColorLine=Class(TLine)
Color: TColor; // Дополнительное поле - цвет линии
Width: Integer; // Дополнительное поле - ширина линии
Constructor Create; // Новый конструктор
End;
• Объявим экземпляр этого класса и обратимся к нему:
Var AColorLine: TColorLine;
Begin
AColorLine:=TColorLine.Create;
AColorLine.Color:=clRed;
AColorLine.Width:=2;
ACoIorLine.X:=20;// Есть отличие доступа от записи
AColorLine.Y:=20
Таким образом, при объявлении класса TColorLine не требуется указывать напрямую поля, наследуемые от класса TLine, как это требовалось бы при использовании вложенных записей.
МЕТОДЫ
Метод - это подпрограмма, которая определена как часть класса и включена в описание этого класса. Набор всех методов определяет операции, которые могут быть выполнены над экземпляром класса. Методы снабжают экземпляры класса поведением и определяют протокол (способ вызова), посредством которого это поведение можно активизировать. Всего существует шесть разновидностей методов объектов:
• методы-процедуры, которые аналогичны самостоятельным процедурам, за исключением того, что они "присоединены" к тому классу, в котором заданы, и могут быть вызваны лишь через какой-либо активный экземпляр этого класса;
• методы-функции, которые возвращают значения и ведут себя так же, как и обычные самостоятельные функции, с той лишь разницей, которая указана для методов-процедур;
• классовые процедуры, которые концептуально даже ближе к обычным самостоятельным процедурам, чем методы-процедуры. Для вызова классовых процедур не требуется экземпляр класса. Эти процедуры объявляются как часть класса и могут вызываться с использованием ссылки на сам класс (т.е. тип), а не на его экземпляр;
• классовые функции, которые аналогичны классовым процедурам, но возвращают результат;
• конструкторы - это специальные методы, ведущие себя аналогично классовым функциям. Они также вызываются с помощью ссылки на класс, в котором они заданы, и возвращают значение, которое является ссылкой на вновь созданного представителя этого класса. Таким образом и создаются экземпляры классов - путем вызова конструктора соответствующего класса;
• деструкторы - это также специальные методы объекта, похожие на методы-процедуры. Они точно также вызываются, как и они, т.е. необходимо использовать экземпляр класса. Предназначены деструкторы для уничтожения экземпляров класса.
Примечания:
• Все методы описываются в программе дважды:
• первоначально в объявлении класса располагается (как правило, в интерфейсе модуля) объявление метода, которое служит для того, чтобы сообщить компилятору о намерении создать метод. Это подобно объявлению Forward для обычных процедур и функций;
• тело метода размещается вне объявления класса - в разделе реализации (Implementation) того же модуля, либо в программе или в библиотеке, до основного блока Begin End.
• Внутри одного класса можно объявить столько методов, сколько
требуется.
• Внутри методов можно обращаться к методам предков, используя
конструкцию:
[Self.]<имя метода>[(<параметры>)];
• При объявлении методы разных видов могут быть перемешаны между собой.
• Объявления методов могут быть сгруппированы в разделы, важно лишь, чтобы в пределах каждого раздела в описании класса все поля объявлялись до объявления методов.
• За именем метода может следовать заключенный в скобки необязательный список параметров, синтаксис написания которого аналогичен
обычным подпрограммам.
• Базовый класс TObject включает до 25 (Delphi 5) различных методов:
конструктор, деструктор, метод Free, 9 классовых методов и не имеет полей данных и свойств.
• По умолчанию все методы являются статическими. При объявлении метода может быть использовано несколько команд (слов-директив), конкретизирующих вариант вызова метода: Class, Virtual, Dynamic, Abstract, Override, Overload, Reintroduce, Message.
» В Delphi 4.0 в класс TObject введены два виртуальных метода AfterConstruction и BeforeDestruction, которые можно использовать тогда, когда конструктора и деструктора недостаточно.
Методы-функции и методы-процедуры
Методы-процедуры и методы-функции объявляются так же, как и обычные процедуры и функции, с той лишь разницей, что это делается не в блоке объявлений программы (до главного Begin), а в описании класса.
а) Синтаксис объявления процедур и функций:
Type
<имя класса>=Class[(<имя родительского класса»)]
Procedure <имя процедуры>[(<параметры>)];
Function <имя функции>[(<параметры>)]: <тип результата>;
End;
Type TPictureShow=Class // Кинотеатр
FFilm: TFilm; // Поле косвенного доступа – фильм
FTitle: String; // Поле прямого доступа – название
Function GetFilm: TFilm;
Procedure SetFilni(Const AnFiIni: TFilm);
Function GetTitle: String;
Procedure SetTitle(AnTitle: String);
End;
б) Реализация методов.
Синтаксис реализации методов-процедур и методов-функций:
Procedure <имя класса>.<имя процедуры>[(<параметры>}];
[<блок объявлении»}
Begin
<Исполняемые операторы>
End;
Function <имя класса>.<имя функции>[(<параметры>)}:<тип результата>
[<блок объявлений>]
Begin
Исполняемые операторы> Rеsи1t:=<возвращаемое значение>;
Исполняемые операторы> End;
Function TPictureShow.GetTitle: String;
Begin
Result:=FTitle;
End;
Procedure TPictureShow.SetTitle(AnTitle: String);
Begin
FTitle:=AnTitle;
End;
Примечания:
• Реализация метода начинается с указания зарезервированного слова Procedure\Function, за которым следует полное имя метода и параметры:
<имя класса>.<имя метода>[{<параметры>)];
Для метода-функции следует указать и <тип резулътата>.
• Как всегда в теле функции должен быть хотя бы один оператор, присваивающий идентификатору функции либо предопределенной переменной Result возвращаемое значение.
• Внутри метода есть доступ не только к обычным идентификаторам, но и к полям экземпляра класса, инкапсулированным при его определении, с помощью скрытого параметра Self, который передается в подпрограммыранее других параметров.
в) Вызов методов.
Поскольку методы присоединены к определенным классам, они не могут быть просто вызваны тем же способом, что и самостоятельные подпрограммы. Они могут быть активизированы только с помощью экземпляра того класса, в котором они определены. А это значит, что экземпляр класса должен существовать. После того, как экземпляр создан, можно вызвать любые его методы.
Синтаксис вызова метода следующий:
<имя объекта>.<имя метода>[(<параметры>)];
Примечание:
• Вместо полного имени можно использовать также и операторWith, причем не требуется явно передавать методу активный экземпляр класса в параметре, поскольку это выполняется Delphi автоматически.