Методы-функции и методы-процедуры

ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ

Часть 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 автоматически.