Уточните пожалуйста, какие документы для этого необходимо предоставить с моей стороны. Курс "Объектно-ориентированное программирование и программная инженения". |
События
Терминология
Для рассматриваемого стиля программирования важно тщательно определить концепции, обращая, как обычно, внимание на различение типов и их экземпляров. События, издатели и подписчики
Определение: событие
Это определение высвечивает отличительные свойства событий.
- Событие сопровождается некоторой информацией: при щелчке мыши должна указываться позиция курсора, при изменении температуры — старая и новая температуры.
- Всегда включается информация о том, какое событие произошло:
- 5-го августа 1492 года Христфор Колумб отправился в плавание;
- 5 минут назад (менее известное событие) я щелкнул левую кнопку моей мыши.
- Обычно информации больше — куда отправился Колумб, каковы координаты мыши. Но иногда достаточно знать, что событие произошло, например, что истек срок ожидания.
- Определенные элементы ПО могут использовать эту информацию.
- Во всех случаях важно то, что само событие не знает своих получателей (писатель не знает читателей). В противном случае этот механизм не отличался бы от механизма вызова методов, подобно вызову x.f (a, b, c), который удовлетворяет всем другим свойствам определения — поставляет информацию (a, b, c), доступную элементу ПО (методу f ). Но когда вызывается метод, то явно указывается адресат вызова. При вызове событий все не так — информация посылается в межадресное пространство, где любой элемент может использовать ее в интересах своей работы.
Помните, что для наших целей событие — это операция ПО. Могут существовать внешние события — действия пользователя и прочее, — приводящие к возникновению программных событий, но и сама программа может создавать события в процессе работы.
Рассмотрим связанную с этими понятиями терминологию, неформально уже встречавшуюся.
Определения: включение, публикация, издатель, подписчик
Один и тот же элемент ПО может быть издателем и подписчиком, в частности, благодаря общей для подписчиков схеме — включать собственное событие как реакцию на возникновение пришедшего к нему события.
В литературе встречаются синонимы приведенных выше терминов: подписчики называются "наблюдателями", отсюда образец "Наблюдатель", изучаемый в этой лекции. Говорят также о "слушателях" события. Для "издателей" также используется синоним, вытекающий из подобной риторики — "предмет наблюдения" .
Аргументы и типы событий
Нам необходимо имя для информации, приходящей — в соответствии с определением — с любым событием.
Определение: аргумент
Термин "аргумент" указывает на сходство с аргументами метода. Продолжая это сходство, будем предполагать, что аргументы сгруппированы в упорядоченный список, подобно аргументам в вызове x.f (a, b, c) . Как и для методов, список может быть пустым, например, в случае события, указывающего на истечение срока ожидания.
Как подписчики обнаруживают, что произошло интересующее их событие? Одна модель — опрос (повторяющаяся проверка). Это напоминает ситуацию с подписчиком журнала, который в день издания ходит к почтовому ящику, чтобы проверить, не пришел ли журнал. Вторая модель — уведомление: при включении события об этом уведомляются все подписчики.
Модели для информации, распределенной в сети Интернет, классифицируются как "притяжение" (ожидание пользователей, заинтересованных в информации) и "проталкивание" (рассылка информации пользователям).
Модель уведомления — более гибкая, и далее будем предполагать именно ее. Она может работать при условии, что подписчики заранее явно проявили свой интерес. Дело обстоит так же как с подписчиками журнала: на журнал надо заранее подписаться. Но на что можно подписаться? На событие подписаться нельзя, поскольку, по определению, это операция, выполняемая в ходе работы программы: до его появления оно не существует, а после — уже поздно.
То, что нужно знать подписчикам, это тип события, описывающий возможные события, разделяющие общие характеристики. Например, все щелчки левой кнопки мыши — это события одного типа, но отличающиеся от конкретного события нажатия клавиши. Понятие типа события играет центральную роль в проектировании, управляемом событиями, и будет представлять центральную абстракцию в нашем поиске хорошей ОО-архитектуры.
Все события одного типа имеют один и тот же список аргументов. Например, список аргументов для события — щелчок левой кнопки мыши — включает координаты мыши в момент щелчка — два целых числа. Во многом концепция заимствована у методов, обладающих сигнатурой — списком типов аргументов. Процедура print (v: VALUE; f: FORMAT) имеет сигнатуру [VALUE, FORMAT] — список типов. Это понятие расширяется на типы события.
Определения: тип события, сигнатура
Все события данного типа события имеют одну и ту же сигнатуру. Например:
- сигнатура события "изменение температуры" может быть [REAL, REAL], чтобы представлять старое и новое значения;
- событие "левый щелчок мыши" может иметь сигнатуру [INTEGER, INTEGER] .
Можно также взять событие "одиночный щелчок мыши" с третьим компонентом сигнатуры, указывающим, какая кнопка была нажата. В библиотеке EiffelVision применяется вариант, в котором добавлен еще один аргумент, показывающий, остается ли нажатой кнопка, - он полезен (особенно в играх) для джойстика и экзотичных устройств указателей;
- хотя можно было бы определить тип события для каждой клавиши на клавиатуре, более удобно использовать один тип события "клавиша нажата" с сигнатурой [CHARACTER], где аргумент задает код клавиши;
- для событий без аргументов, например, "исчерпано время ожидания", сигнатура пуста, как у методов без аргументов.
Всякий раз, когда издатель включает событие, он должен обеспечить значение каждого аргумента (если они есть): координаты мыши, код клавиши, температуры. И здесь наблюдается полная аналогия с вызовом метода, где при каждом вызове задаются фактические аргументы.
Термин "тип события" может предполагать еще одну аналогию, где каждый тип соответствует типам ОО-программирования (классам с возможно родовыми параметрами), а каждое событие соответствует экземпляру класса (объектам). Но сравнение типов событий с программами - более показательное, тогда появление события данного типа соответствует вызову программы.
При таком подходе событие — это не объект, а тип события — это не класс. Вместо этого можно ввести общее понятие для всех типов событий: класс, называемый ниже EVENT_TYPE, а конкретный тип события, например, "щелчок левой кнопки мыши" (абстракция левых щелчков — вроде того, что я в прошлый понедельник, будучи в расстроенных чувствах, щелкнул в ответ на запрос "Delete all?" ) рассматривать как объект. Как всегда, когда вы раздумываете, не ввести ли класс, критерием является "Можно ли эту абстракцию данных наполнить смыслом, определив множество хорошо понимаемых операций, применимых ко всем объектам класса?". В данном случае:
- если бы мы решили создавать класс для типа события, его экземплярами были бы события одного типа, но у них не было бы полезных компонентов. Более точно, события имели бы собственные данные — аргументы, но они нуждались бы только в запросах для получения доступа к этим аргументам; команд здесь не было бы;
- в противоположность такому подходу: если рассматривать конкретный тип события как объект, то появляется несколько хорошо определенных команд и запросов — включить событие с заданным набором аргументов, подписать данного подписчика на этот тип события, удалить подписчика из списка подписчиков, перечислить всех подписчиков, подсчитать число включений события данного типа и так далее. Это богатое множество компонентов характеризует полезный законный класс.
Не рассматривать каждое событие как объект полезно и с позиций производительности. Обычно во время выполнения создается большое число событий - каждое малое перемещение курсора включает событие, так что следует избегать создания всех соответствующих объектов. Это не освобождает нас от нагрузки, поскольку аргументы каждого события, представленные кортежем, должны быть записаны. В хорошей библиотеке GUI производительность улучшается за счет того, что для последовательности близких событий можно использовать один кортеж вместо десятков сотен.
Полезно иметь термины для действий подписчиков с типами событий и событиями.
Определения: подписать, зарегистрировать, обработать, захватить
Элемент ПО может стать подписчиком некоторого типа событий, подписавшись (зарегистрировавшись) на него. После регистрации элемент будет получать уведомления о всех возникающих событиях этого типа, так что он может получить аргументы и выполнить в ответ специфические действия.
Когда элемент получает уведомление о событии, на которое он подписан, он обрабатывает (или захватывает ) событие, выполняя зарегистрированное действие.
Хотя регистрацию (дерегистрацию) можно выполнить в любой момент, общепринято иметь фазу инициализации, расставляя подписчиков по местам, после чего уже выполняется главный этап выполнения, на котором издатели включают события, а подписчики их обрабатывают.
При регистрации подписчик задает некоторое действие, выполняемое в ответ на возникновение события данного типа. У действия должен быть способ получения аргументов события. Очевидный путь достижения цели — это указание при регистрации метода, чья сигнатура соответствует сигнатуре типа события. Тогда возникновение события данного типа будет причиной вызова метода, обрабатывающего событие, где аргументы события играют роль фактических аргументов вызова.
Теперь у нас есть полная картина того, как работает программа, управляемая событиями.
Схема управления событиями
E1 Некоторые элементы, издатели, позволяют остальной системе узнать, какие типы событий они могут включать.
E2 Некоторые элементы, подписчики, заинтересованы в обработке событий определенных типов. Они регистрируют соответствующие действия.
E3 В любой момент издатель может включить событие. Это приведет к выполнению действий, зарегистрированных подписчиками для события данного типа. Эти действия могут использовать аргументы события.
В примере GUI:
E1 Издателем является некоторый элемент ПО, который следит за устройствами ввода и включает события при определенных обстоятельствах, например, при нажатии клавиш клавиатуры или кнопок мыши. Обычно нет необходимости писать такое ПО, поскольку оно является частью библиотеки GUI - EiffelVision для Eiffel, Windows Forms для .NET, Swing для Java.
E2 Подписчиком является любой элемент, которому требуется обработать GUI-события. Он регистрирует методы, выполняемые в ответ на события. Например, метод, сохраняющий файл, можно зарегистрировать для события щелчок мыши на кнопке с надписью "OK" в диалоге по сохранению файла.
E3 Если во время выполнения пользователь щелкнет по кнопке OK, то это станет причиной выполнения метода — или методов, — зарегистрированных для данного типа события.
Важное свойство этой схемы, проиллюстрированное на последнем рисунке: выделение двух сторон, участвующих в создании и обработке события, — подписчики и издатели ничего не знают друг о друге. Более точно, определение "события" требует, чтобы подписчики не знали других подписчиков. Остальное — дело методологии, и мы увидим, как различные архитектурные решения достигают результата по отношению к этому критерию.