- Объектно-ориентированная программа как программа, управляемая событиями
- Событие
- Методы обработки событий
- Главный цикл обработки событий (метод Execute)
- Пример обработки событий
При использовании ООП все объекты являются в некотором смысле обособленными друг от друга, и возникают определенные трудности в передаче информации от объекта к объекту. В ООП для передачи информации между объектами используется механизм обработки событий.
События лучше всего представить себе как пакеты информации, которыми обмениваются объекты и которые создаются объектно-ориентированной средой в ответ на те или иные действия пользователя. Нажатие на клавишу или манипуляция мышью порождают событие, которое передается по цепочке объектов, пока не найдется объект, знающий, как обрабатывать это событие. Для того чтобы событие могло передаваться от объекта к объекту, все объекты программы должны быть объединены в группу. Отсюда следует, что прикладная программа должна быть объектом-группой, в которую должны быть включены все объекты, используемые в программе.
Таким образом, объектно-ориентированная программа – это программа, управляемая событиями. События сами по себе не производят никаких действий в программе, но в ответ на событие могут создаваться новые объекты, модифицироваться или уничтожаться существующие, что и приводит к изменению состояния программы. Иными словами все действия по обработке данных реализуются объектами, а события лишь управляют их работой.
Принцип независимости обработки от процесса создания объектов приводит к появлению двух параллельных процессов в рамках одной программы: процесса создания объектов и процесса обработки данных.
Это означает, что действия по созданию, например, интерактивных элементов программы (окон, меню и пр.) можно осуществлять, не заботясь о действиях пользователя, которые будут связаны с ними.
И наоборот, мы можем разрабатывать части программы, ответственные за обработку действий пользователя, не связывая эти части с созданием нужных интерактивных элементов.
Событие с точки зрения языка С++ – это объект, отдельные поля которого характеризуют те или иные свойства передаваемой информации, например:
{
int what;
union
{
MouseEventType mouse;
KeyDownEvent keyDown;
MessageEvent message;
};
Объект Event состоит из двух частей. Первая (what) задает тип события, определяющий источник данного события. Вторая задает информацию, передаваемую с событием. Для разных типов событий содержание информации различно. Поле what может принимать следующие значения:
-
evNothing – это пустое событие, которое означает, что ничего делать не надо. Полю what присваивается значение evNothing, когда событие обработано каким-либо объектом.
-
evMouse – событие от мыши.
Событие от мыши может иметь, например, такую структуру:
{
int buttons;
int doubleClick;
TPoint where;
};
где buttons указывает нажатую клавишу; doubleClick указывает был ли двойной щелчок; where указывает координаты мыши.
- evKeyDown – событие от клавиатуры.
Событие от клавиатуры может иметь, например, такую структуру:
{
union
{
int keyCode;
union
{
char charCode;
char scanCode;
};
};
};
- evMessage - событие-сообщение от объекта.
Для события от объекта (evMessage) задаются два параметра:
command – код команды, которую необходимо выполнить при появлении данного события;
infoPtr – передоваемая с событием (сообщением) информация.
{
int command;
void *infoPtr;
};
Следующие методы необходимы для организации обработки событий (названия произвольны).
GeEvent – формирование события;
Execute реализует главный цикл обработки событий. Он постоянно получает событие путем вызова GeEvent и обрабатывает их с помощью HandleEvent. Этот цикл завершается, когда поступит событие «конец».
HandleEvent – обработчик событий. Обрабатывает каждое событие нужным для него образом. Если объект должен обрабатывать определенное событие (сообщение), то его метод HandleEvent должен распознавать это событие и реагировать на него должным образом. Событие может распознаваться, например, по коду команды (поле command).
ClearEvent очищает событие, когда оно обработано, чтобы оно не обрабатывалось далее.
Обработчик событий (метод HandleEvent).
Получив событие (структуру типа Event), обработчик событий для класса DerivedClass обрабатывает его по следующей схеме:
{
// Вызов обработчика событий базового класса
BaseClass::handleEvent(event);
if (event.what == evCommand) // Если обработчик событий базового
// класса событие не обработал
{
switch (event.message.command)
{
case cmCommand1:
// Обработка команды cmCommand1
// Очистка события
СlearEvent(event);
break;
case cmCommand2:
// Обработка команды cmCommand2
СlearEvent(event);
break;
...
case cmCommandN:
// Обработка команды cmCommandN
СlearEvent(event);
break;
default: // событие не обработано
break;
}
}
}
Обработчик событий группы вначале обрабатывает команды группы, а затем, если событие не обработано, передает его своим элементам, вызывая их обработчики событий.
{
if (event.what == evCommand)
{
switch (event.message.command)
{
// обработка событий объекта-группы
default: // событие группой не обработано
// получить доступ к первому элементу группы
while ((event.what != evNothing) || (/* просмотрены не все элементы */)
{
// вызвать HandleEvent текущего элемента
// перейти к следующему элементу группы
}
break;
}
}
}
Метод ClearEvent - очистка события.
ClearEvent очищает событие, присваивая полю event.What значение evNothing.
Главный цикл обработки событий реализуется в методе Execute главной группы-объекта "прикладная программа" по следующей схеме:
{
do
{
endState = 0;
GeEvent(event); // получить событие
HandleEvent(event); // обработать событие
if (event.what != evNothing) // событие осталось не обработано
EventError(event);
}
while (!Valid());
endState;
}
Метод HandleEvent программы обрабатывает событие "конец работы", вызывая метод EndExec. EndExec изменяет значение private – переменной EndState. Значение этой переменной проверяет метод–функция Valid, возвращающая значение true, если "конец работы". Такой несколько сложный способ завершения работы программы связан с тем, что в активном состоянии могут находиться несколько элементов группы. Тогда метод Valid группы, вызывая методы Valid своих подэлементов, возвратит true, если все они возвратят true. Это гарантирует, что программа завершит свою работу, когда завершат работу все ее элементы.
Если событие осталось не обработанным, то вызывается метод EventError, которая в простейшем случае может просто выдать сообщение.
Рассмотрим простейший калькулятор, воспринимающий команды в командной строке.
Формат команды:
знак параметр
Знаки +, –, *, /, =, ?, q
Параметр – целое число
Константы-команды
const int evMessage = 100;
сonst int cmSet = 1; // занести число
сonst int cmGet = 2; // посмотреть значение
const int cmAdd = 3; // добавить
и т.д.
const int cmQuit = 101; // выход
Класс-событие
{
int what;
union
{
int evNothing;
union
{
int command;
int a;
}
}
};
Объект-калькулятор, работающий с целыми числами.
{
public:
Int(int x1);
virtual ~Int();
virtual void GeEvent(Event &event);
virtual int Exicute(void);
virtual void HandleEvent(Event &event);
virtual void ClearEvent(Event &event);
int Valid(void);
void EndExec(void);
int GetX(void);
void SetX(int newX);
void AddY(int Y);
...
protected:
int x;
int EndState;
};
Рассмотрим возможную реализацию основных методов.
{
char *OpInt = "+-*/=?q"; //строка содержит коды операций
char s[20];
char code;
cout << '>';
cin >>s code = s[1];
if (Test(code, OpInt) // Функция Test проверяет, входит ли
// символ code в строку OpInt
{
event.what = evMessage;
switch (code)
{
case '+': event.command = cmAdd; break;
...
case 'q': event.command = cmQuit; break;
}
// выделить второй параметр, перевести его в тип int и присвоить полю A
}
else
{
event.what = evNothing;
}
};
int MyApp::Execute(void)
{
do
{
endState = 0;
GeEvent(event); // получить событие
HandleEvent(event); //обработать событие
if (event.what != evNothing) // событие осталось не обработано
{
// обработать событие
}
}
while (!Valid());
return endState;
}
void Int::HandleEvent(Event &event)
{
if (event.what == evMessage)
{
switch (event.message.command)
{
case cmAdd:
AddY(event.A);
СlearEvent(event);
break;
...
case cmQuit:
EndExec();
СlearEvent(event);
break;
}
}
}
int Int::Valid(void)
{
if (EndState) return 1;
else return 0;
}
void Int::ClearEvent(Event &event)
{
event.what = evNothing;
}
void Int::EndExec(void)
{
EndState = 1;
}
void Int::AddY(int Y)
{
x += Y;
}
...
int main(void)
{
Int MyApp;
MyApp.Execute();
return 0;
}