Событие в объектно-ориентированное программировании (ООП) — это сообщение, которое возникает в различных точках исполняемого кода при выполнении определённых условий. Данные сообщения направляются обработчикам (слушателям), что позволяет своевременно реагировать на изменившееся состояние системы.
Большой популярностью пользуется событийно-ориентированное программирование. Эта парадигма программирования говорит, что выполнение программы определяется событиями — действиями пользователя (клавиатура, мышь), сообщениями других программ и потоков, событиями операционной системы и т.д.
Преимущества и сферы применения событийной модели чрезвычайно широки:
Существуют шаблоны проектирования, так или иначе связанные с обработкой событий: Наблюдатель, Команда, Цепочка обязанностей и многие другие. Данные шаблоны используются во многих моделях обработки событий. Однако различные реализации событийной модели накладывают ряд ограничений на возможности разработки программ:
Перечень перечисленных ограничений не является полным, однако указанные ограничения являются наиболее существенными. В данной статье предлагается реализация более сложной событийной модели, которая позволит снять все перечисленные ограничения.
При разработке программного обеспечения с использованием данной модели обработки событий последовательность этапов обработки следующая:
Рассмотрим преимущества данной модели:

На картинке представлена схема реализации данной модели. Эта реализация изначально опирается на работу с использованием цепочки вызовов. Ниже представлен пример реализации модели на языке PHP.
Рассмотрим пример работы данной модели:
После инициализации события sampleEvent вызовется метод Fire1 слушателя $listener с первыми данными (sort=10, именно этот приоритет он и поставит на финальную обработку), а затем этот же метод вызовется с другими данными (sort=20, 20>10, следовательно слушатель с этими данными и получит право финальной обработки события). В заключении вызовется метод Fire2 (Обработчик тот же, но данные $data2). Данные от события (sort=30) складываются с данными от слушателя (sort=20). В итоге событие в ответ вернет нам число 50, которое и будет выведено на экран.
Данный пример показывает, что один и тот же обработчик может по разному реагировать на событие в зависимости от данных слушателя. Также он демонстрирует процесс обработки события, включая результирующий, единственный вызов Fire2 (Представьте что именно в этом методе, например, генерируется дизайн страницы сайта, а данные – это внешний контекст обработки события).
Реализовав данную модель обработки событий удалось снять все указанные выше ограничения. В настоящее время модель ориентирована на конкретный продукт (имеются паразитивные методы необходимые только в этом продукте), но если будет интерес, то можно подготовить код к публичной версии.
Также можно реализовать данную модель и на других языках программирования. В cpp можно в качестве данных передавать указатель, а в методах обработки использовать reinterpret_cast или воспользоваться шаблонами.
Реализация в каждой конкретной ситуации может немного отличаться, однако основной смысл именно в реализации двух или более уровней.
Большой популярностью пользуется событийно-ориентированное программирование. Эта парадигма программирования говорит, что выполнение программы определяется событиями — действиями пользователя (клавиатура, мышь), сообщениями других программ и потоков, событиями операционной системы и т.д.
Преимущества и сферы применения событийной модели чрезвычайно широки:
- при связывании объектов событиями, объектам нет необходимости «знать» друг о друге;
- данный механизм четко вписывается в концепцию ООП;
- данный механизм позволяет легко реализовывать пользовательские интерфейсы, где каждое действие пользователя – событие, достаточно «навесить» на эти события обработчики;
- при реализации серверных приложений событийная модель позволяет избавиться от порождения множества обслуживающих процессов;
- событийная модель также часто используется при программировании игр, в которых осуществляется управление множеством объектов.
Существуют шаблоны проектирования, так или иначе связанные с обработкой событий: Наблюдатель, Команда, Цепочка обязанностей и многие другие. Данные шаблоны используются во многих моделях обработки событий. Однако различные реализации событийной модели накладывают ряд ограничений на возможности разработки программ:
- при обраб��тке события нельзя выделить главный обработчик, все обработчики равны между собой;
- при обработке события обработчики ничего не знают друг о друге и о сложившейся ситуации, что не позволяет программисту полностью реализовать задуманный функционал;
- приходится дописывать дополнительный функционал, если требуется выбрать результирующий обработчик.
Перечень перечисленных ограничений не является полным, однако указанные ограничения являются наиболее существенными. В данной статье предлагается реализация более сложной событийной модели, которая позволит снять все перечисленные ограничения.
При разработке программного обеспечения с использованием данной модели обработки событий последовательность этапов обработки следующая:
- Необходимые слушатели подключаются на обработку своих событий.
- В программном коде инициируется событие.
- Выполняется «первый круг» обработки, всем слушателям подключившимся на обработку данного события предоставляется время на первичную обработку, в процессе которой каждый слушатель может получить собственные данные, данные от инициатора события, а также установить какие-либо данные для передачи во «второй круг» и подать заявку на обработку события во «втором круге».
- Среди всех слушателей данного события выбирается слушатель, который подал заявку с наибольшим приоритетом обработки, именно ему предоставляется право на обработку события во «втором круге». В процессе данной обработки он может получить данные от инициатора, собственные данные, данные от собственн��го обработчика «первого круга». После завершения обработки он может вернуть данные непосредственно инициатору события.
Рассмотрим преимущества данной модели:
- Возможно «навешивать» один и тот же обработчик на разные события или даже на одно и тоже, но с разными собственными данными.
- Возможно обрабатывать наиболее широкий круг событий. Например, добавление нового пользователя, вывод дизайна страницы сайта и т.д. В данном случаи событие добавления нового пользователя можно обработать используя лишь «первый круг», а событие вывода дизайна страницы сайта необходимо обрабатывать в «два круга».
- Выполняется лишь один обработчик «второго круга» (Рассмотрим событие вывода дизайна сайта: на первом круге обработчики анализируя состояние системы выдвигают приоритеты обработки события и лишь на «втором круге» один из них генерирует дизайн страницы сайта).

На картинке представлена схема реализации данной модели. Эта реализация изначально опирается на работу с использованием цепочки вызовов. Ниже представлен пример реализации модели на языке PHP.
- Events – основной класс. Лишь его объект пользователь может создать сам. Данный класс предоставляет возможность инициировать события и «навешивать» обработчики событий.
- newListener – позволяет добавить нового слушателя и тут же «настроить» его (добавление происходит через адаптер для Events_Listener – Events_Listener_adapterSET).
- newEvent – позволяет сконфигурировать новое событие, а затем инициировать его (данное действие происходит через адаптер для Events_Fire – Events_Fire_adapterSET).
К процессу работы прозрачно для пользователя подключаются и другие классы:
- Events_Fire – предоставляет общий интерфейс работы с событием, который ограничивают адаптеры;
- Events_Fire1 – при вызове первого круга обработки. Данный класс позволяет получить все имеющиеся данные текущего состояния, добавить обработчик второго круга, а также установить его приоритет (через адаптер Events_Fire2_adapterSET);
- Events_Fire2 – при вызове результрующего обработчика («второй круг»). Этот класс позволяет получить все имеющиеся данные, включая данные от обработчика «первого круга»;
- Events_Listener — предоставляет общий интерфейс работы с обработчиком события. Методы данного класса также используют адаптеры Events_Fire1, Events_Fire2;
- Events_Data – класс для хранения и передачи данных внутри событийной модели.
Рассмотрим пример работы данной модели:
class SampleListener
{
public function Fire1(Event_fire1 $EF1)
{
$EF1->setFinal(Array($this,”Fire2”), $EF1->getListenerData()->sort);
}
public function Fire2(Event_fire2 $EF2)
{
return ($EF2->getListenerData()->sort() + $EF2->getFireData()->sort());
}
}
$listener = new SampleListener();
$data1 = new Events_Data();
$data1->sort = 10;
Events::newListener("sampleModul", "sampleEvent", Array($listener, 'Fire1'))->setData($data1);
$data2 = new Events_Data();
$data2->sort = 20;
Events::newListener("sampleModul", "sampleEvent", Array($listener, 'Fire1'))->setData($data2);
$data3 = new Events_Data();
$data3->sort = 30;
Echo Events::newEvent("sampleModul", "sampleEvent")->setData($data3)->fire();После инициализации события sampleEvent вызовется метод Fire1 слушателя $listener с первыми данными (sort=10, именно этот приоритет он и поставит на финальную обработку), а затем этот же метод вызовется с другими данными (sort=20, 20>10, следовательно слушатель с этими данными и получит право финальной обработки события). В заключении вызовется метод Fire2 (Обработчик тот же, но данные $data2). Данные от события (sort=30) складываются с данными от слушателя (sort=20). В итоге событие в ответ вернет нам число 50, которое и будет выведено на экран.
Данный пример показывает, что один и тот же обработчик может по разному реагировать на событие в зависимости от данных слушателя. Также он демонстрирует процесс обработки события, включая результирующий, единственный вызов Fire2 (Представьте что именно в этом методе, например, генерируется дизайн страницы сайта, а данные – это внешний контекст обработки события).
Реализовав данную модель обработки событий удалось снять все указанные выше ограничения. В настоящее время модель ориентирована на конкретный продукт (имеются паразитивные методы необходимые только в этом продукте), но если будет интерес, то можно подготовить код к публичной версии.
Также можно реализовать данную модель и на других языках программирования. В cpp можно в качестве данных передавать указатель, а в методах обработки использовать reinterpret_cast или воспользоваться шаблонами.
Реализация в каждой конкретной ситуации может немного отличаться, однако основной смысл именно в реализации двух или более уровней.