- •Предисловие
- •Введение Эволюция разработки программного обеспечения
- •Технологии программирования
- •Основные понятия объектно-ориентированного программирования
- •Инкапсуляция
- •Свойства
- •Векторные свойства
- •Создание и уничтожение объектов
- •Конструкторы
- •Деструкторы
- •Наследование
- •Свойства
- •Конструкторы и деструкторы класса-предка
- •Полиморфизм, виртуальные и динамические методы
- •Статическое перекрытие виртуальных методов
- •Виртуальное перекрытие конструкторов и деструкторов
- •Абстрактные методы
- •Области видимости
- •Перекрытие и переопределение свойств
- •Перекрытие методов доступа к свойствам
- •Приведение объектных типов, операторы as и is
- •Агрегация
- •События
- •Процедурный тип
- •Создание события
- •Инициаторы события
- •Делегирование
- •Внутреннее устройство объекта
- •Указатели на класс
- •Виртуальные конструкторы
- •Методы класса
- •Обработка исключительных ситуаций
- •Операторы try...Except и try...Finally
- •Исключительные ситуации как объекты
- •Перегрузка методов
- •Перегрузка виртуальных методов
- •Параметры по умолчанию
- •Основы объектно-ориентированного анализа и проектирования
- •Объектно-ориентированная модель
- •Классы и объекты
- •Заключение Применение объектно-ориентированного программирования
- •Библиографический список
Создание события
Как было сказано выше, для того, чтобы объект мог получать от другого объекта уведомления о произошедших изменениях, он должен сообщить ему, какой метод следует вызывать при необходимости. Для этого в объекте, который является источником уведомлений, создаются так называемые события.
Событие – это свойство типа указатель на метод. Методы чтения и записи свойства для события, как правило, не используются. Указывается непосредственно имя поля, в котором будет храниться указатель на метод.
Присвоить такому свойству значение – значит, указать объекту адрес метода - обработчика события, который будет вызываться в момент наступления события. Класс - инициатор события должен вызывать этот метод, когда событие наступило.
Для того чтобы организовать событие у класса - инициатора события, следует:
-
Объявить тип события, если только не используется один из стандартных типов библиотеки Borland Delphi. При определении собственного типа следует помнить следующее. Хотя компилятор позволяет объявить тип указатель на метод-функцию, делать этого не следует. Поскольку пустая функция возвращает неопределенный результат, пустой обработчик события-функции может не всегда работать корректно. Таким образом, все события должны быть свойствами, имеющими тип указатель на метод-процедуру. Если имеется необходимость возвращать значения из обработчика события, то это делается через параметры, передаваемые по имени;
-
Объявить поле соответствующего типа для хранения адреса метода. Как правило, полю дают имя, совпадающее с именем события, но начинающееся с F, например FOnMyEvent;
-
Объявить собственно событие – свойство типа указатель на метод. По традиции имена событий начинают с On: OnMyEvent, OnMouseClick, OnDraw и т.п.
-
Организовать запуск события. При этом следует проверять, назначен ли обработчик события и если назначен, то вызывать его. Для проверки можно использовать функцию function Assigned(var P): Boolean, которая возвращает истину, если указатель P не равен nil, то есть указатель указывает на какой-либо метод.
Например:
interface Type
//тип события TMyEvent = procedure of object;
//класс – инициатор события TSomeSourceEventObject = class(TObject) private //поле FOnMyEvent: TMyEvent; ... protected //метод, из которого будет запускаться событие procedure SomeMethod; ... public //само событие Property OnMyEvent: TMyEvent read FOnMyEvent write FOnMyEvent; ... end; ...
implementation procedure TSomeSourceEventObject.SomeMethod; begin if (SomeCondition) then //некоторое условие наступления события begin //если назначен обработчик события if Assigned(FOnMyEvent) then //запускаем его FOnMyEvent; end; end;
В классе - получателе события следует создать метод - обработчик события. Также где-то следует присвоить указатель на этот метод соответствующему событию объекта класса - инициатора события. Например:
interface Type
TSomeDestinationEventObject = class(TObject) ... public //метод – обработчик события procedure SomeObjectEvent; //метод, устанавливающий обработчик события procedure SetEvent(Source: TSomeSourceEventObject); ... end; ...
implementation procedure TSomeDestinationEventObject.SetEvent(Source: TSomeSourceEventObject); begin ... //назначаем обработчик события Source.OnMyEvent := SomeObjectEvent; ... end;
В этом примере указатель на объект - инициатор события передается как параметр метода объекта, желающего получать уведомления. Также он мог бы быть глобальной переменной или быть агрегирован в объект, получающий уведомления.
Один из примеров использования механизма событий – взаимодействие визуальных компонентов Borland Delphi, таких как кнопка или окно редактирования, с формой. Кнопка является инициатором события. Причем класс кнопки остается неизменным. При разработке графического интерфейса создается новый класс формы, именно в котором, как правило, и определяются методы - обработчики событий.
Рассмотрим другой пример. Допустим, мы занимаемся моделированием. Первый из элементов модели – горелка газовой плиты TBurner. Другой – то, что на эту горелку поставили. Это может быть чайник TKettle или, например, кастрюлька TSaucepan. Интересующие нас события – включение горелки OnBurn. Причем не известно заранее, какой объект следует уведомлять об этом событии – чайник или кастрюльку. У класса TBurner должно быть объявлено соответствующее событие и организован его запуск:
interface Type
//тип события TOnBurn = procedure of object;
//класс - горелка TBurner = class(TObject) private //поле FOnBurn: TOnBurn; ... protected //метод включения горелки procedure SwitchOn; ... public //само событие Property OnBurn: TOnBurn read FOnBurn write FOnBurn; ... end;
//класс - чайник TKettle = class(TObject) ... public //метод – обработчик события – модель закипания чайника procedure Boil; ... end;
//класс - кастрюлька TSaucepan = class(TObject) ... public //метод – обработчик события – модель подогрева кастрюльки procedure Heat; ... end;
implementation procedure TBurner.SwitchOn; begin //если назначен обработчик события if Assigned(FOnBurn) then //запускаем его FOnBurn; end;
Предположим, что глобальные объекты горелка Burner, чайник Kettle и кастрюлька Saucepan уже созданы. Тогда действие поставить на плиту чайник выглядит следующим образом:
Burner.OnBurn := Kettle.Boil;
А поставить на плиту кастрюльку:
Burner.OnBurn := Saucepan.Heat;
И в одном случае при включении горелки Burner.SwitchOn будет кипеть чайник, то есть вызовется метод Kettle.Boil, а в другом подогреваться кастрюлька: Saucepan.Heat.
Как правило, метод - обработчик события получает в качестве одного из параметров ссылку на объект, сгенерировавший событие. Это необходимо, поскольку одно и тоже событие может быть сгенерировано разными объектами, и иногда обработчику события надо различать, кто является инициатором этого события. В общем случае этот параметр имеет тип TObject, что позволяет передавать указатель на любой тип объекта. Традиционное имя этого параметра Sender.
Рассмотрим пример: пусть на экране имеется некоторое количество кнопок и окно редактирования текста. То есть объекты типов TButton – кнопка и TEdit – окно редактирования. Кроме того, есть еще один объект класса TStatistics, который должен собирать статистику – сколько раз пользователь за время работы программы щелкал мышью по кнопкам и окнам редактирования. Для этого все кнопки и окна редактирования должны извещать объект сбора статистики, когда для них произошло событие – щелчок мышью. То есть у классов TButton и TEdit должно быть организовано событие – щелчок мышью OnMouseClick а у класса TStatistics метод обработчик события щелчок мыши по чему-то SomethingMouseClick:
Type TMouseClickEvent = procedure(Sender: TObject) of object; TButton = class private FOnMouseClick: TMouseClickEvent; ... public property OnMouseClick: TMouseClickEvent read FOnMouseClick write FOnMouseClick; ... end;
TEdit = class private FOnMouseClick: TMouseClickEvent; ... public property OnMouseClick: TMouseClickEvent read FOnMouseClick write FOnMouseClick; ... end;
TStatistics = class private NumberButtonClick: integer; NumberEditClick: integer; ... public procedure SomethingMouseClick(Sender: TObject); ... end;