Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка ООП.doc
Скачиваний:
23
Добавлен:
08.11.2018
Размер:
1.4 Mб
Скачать

Создание события

Как было сказано выше, для того, чтобы объект мог получать от другого объекта уведомления о произошедших изменениях, он должен сообщить ему, какой метод следует вызывать при необходимости. Для этого в объекте, который является источником уведомлений, создаются так называемые события.

Событие – это свойство типа указатель на метод. Методы чтения и записи свойства для события, как правило, не используются. Указывается непосредственно имя поля, в котором будет храниться указатель на метод.

Присвоить такому свойству значение – значит, указать объекту адрес метода - обработчика события, который будет вызываться в момент наступления события. Класс - инициатор события должен вызывать этот метод, когда событие наступило.

Для того чтобы организовать событие у класса - инициатора события, следует:

  1. Объявить тип события, если только не используется один из стандартных типов библиотеки Borland Delphi. При определении собственного типа следует помнить следующее. Хотя компилятор позволяет объявить тип указатель на метод-функцию, делать этого не следует. Поскольку пустая функция возвращает неопределенный результат, пустой обработчик события-функции может не всегда работать корректно. Таким образом, все события должны быть свойствами, имеющими тип указатель на метод-процедуру. Если имеется необходимость возвращать значения из обработчика события, то это делается через параметры, передаваемые по имени;

  2. Объявить поле соответствующего типа для хранения адреса метода. Как правило, полю дают имя, совпадающее с именем события, но начинающееся с F, например FOnMyEvent;

  3. Объявить собственно событие – свойство типа указатель на метод. По традиции имена событий начинают с On: OnMyEvent, OnMouseClick, OnDraw и т.п.

  4. Организовать запуск события. При этом следует проверять, назначен ли обработчик события и если назначен, то вызывать его. Для проверки можно использовать функцию 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;