| |
Transitioning from Structured to Event-Driven Programming by Ben Watson, 8/17/2002
Introduction
When I started programming, I used Borland C++ 3.1. Without using external libraries that I didn't know how to use anyway, the software I created was in a strictly text-console environment. That is how I learned. When I first got my hands on Borland's Object Windows Library for Windows programming, I was hopelessly lost. The paradigm shift was too much for me, and I didn't understand the new model at all. For that reason I believe this tutorial can help some people understand how to make the transition from old-style techniques to modern event-driven ways of writing code.
Event-driven programming vastly improves on older models because it transfers the flow of the program from the programmer to the user. It is ideal for creating any type of user-focused application.
Structured Programming
The standard method of programming in that environment is structured. That is, we interact with the data (primitives and objects) through control structures such as IF...THEN, WHILE..., SWITCH..., FOR..., etc. The entire program's nature is defined by these structures. Let's look at a simple example in C++:
void main() {
int input=0;
while (input!=-1) {
cout << "Enter an integer (-1 to quit): ";
cin >> input;
}
cout << input;
}
|
This program reads in integers until -1 is entered. It's exceedingly simple, but it demonstrates a fundamental point about structured programming. We are always waiting for the input. The flow of the program is completely controlled by the while loop. The loop provides one way of exiting it--by inputting a specific value. Even extremely large programs based on structures merely extend this concept to larger scales. They still highly-define the user's path through the program.
Event-Driven Programming
What if instead of tightly controlling the run of a program, the software just sits idle until the user does something? In other words, the flow of the program is controlled by user-generated events.
The meat of event-driven programming is in the event-handlers. These are functions in the software that you call in response to certain events
This is probably the most important conceptual part of even-driven programming: the user is in control of your code. This has vast-implications on the type of code you write. For one, you can't expect the user to follow a predefined execution path (unless you go to great pains to enforce one). Instead, the system must be much more open-ended. This means more robust error-handling--you can NEVER assume the user has entered data in a certain order or processed one event over another, etc.
For an example, imagine a window that has two buttons. One of them lets you input an integer, and puts it on a stack. The other button adds every number on that stack. In a structured environment, you could go through a loop, inputing numbers, then adding them. If your program is event-driven, you can't assume a certain number of integers, you can't even assume that any integers have been entered at all.
That's a simplistic example, but it illustrates the fact that even-driven code must be robust enough to handle the results of other events--or, more commonly, what happens when other events haven't happened. Even-driven idioms are non-sequential.
Messaging
How does our program know there is an event? Most modern event-driven programs have something called a message loop. A message loop is usually an internal structure, hidden by the class libraries (FCL with .NET) if you're using an object-oriented system of programming. If you're programming in pure API (such as the Win32 API), then you may have to write the message loop yourself. A simple message loop can look like this:
while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))
{
switch(msg.message)
{
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_KEYDOWN:
fDone = TRUE;
}
}
|
This looks at the next message in the queue, removes it from the queue, and then performs some function based on the contents of that message. In this case, if the message is a mouse-click or a key-press, fDone is set to TRUE.
So how do messages get put in the queue? Who puts them there? This is the job of the operating system. Most modern operating systems completely control the user's interaction with the system. All mouse-clicks, key presses, video-output go through the OS between the user and the application.
This would be a good place to speak of GUIs.
The GUI
The history of the GUI is a long one, frought with complex politics that are very curious to learn about if you're interested. How would all of our lives be different if Xerox had done this, or HP accepted the idea of the Apple, or Bill Gates not given the oppurtunity to write DOS? Interesting things to think about...
Most modern operating systems in wide use rely on the use of a Graphical User Interface to communicate. This does wonders for productivity. Non-programmers/hobbyists can use a computer to get work done.
As it pertains to our discussion, though, a GUI is important because it defines not only how our program looks, but how it interacts with the user. All modern GUIs contain similar elements: forms (also called windows), controls to fill those forms, menus, etc.
A control is both a graphical element and an input/output object. I am writing this in Windows XP, using Notepad, which is a form with a menu, and an edit control which allows me to type in text. This control is created and managed by Windows itself--the code for Notepad merely tells Windows, "Hey, I want to use one of your edit control-thingamajiggies in my program, and I want it to have these options on it." Those options can be setting it to multi-line, a certain font, or whatever options the control supports.
The Windows Edit control ships in a library included in every system. Other operating systems/GUIs may differ in the details, but the point is that there is usually a large library of controls already coded for us that our programs can make use of. This is great! We don't have to reinvent the wheel every time we need an edit control, or a button (also a control). Common controls included with GUIs include: buttons, edit controls, labels, tree-views, lists, status bars, progress bars, etc. A programmer can use any of those, as well as invent his/her own control for a custom need.
Having all of these interface elements part of the operating system is a boon for programmers. Remember DOS? Do you know how much work has to go into creating a usable GUI for DOS? A LOT. You have to write everything from scratch. (Yes, there were proprietary libraries available to simplify the task).
Now that the operating system manages all of our interace, it is the one that recognizes that the user has a clicked a button. The OS gathers all of the information it can about this event, packages it into a structure called the message, and sticks it in our application's message loop, as we looked at above. The only code we have to worry about is responding to these events, creating our program's own logic, and telling the OS to update the interface as necessary.
The code for a full C# implementation of our integer adding example would take too much space here, so I'll write the important functions in C#/pseudo-code. The form is simple: two text boxes, and two buttons. The first button take an integer input from the first text box, and puts in a list of integers. The second button adds up all the integers in the list and displays the result in the second text box.
void OnPutIntegerInList() {
if (textbox1.Text=="")
DisplayError();
else
integerList.Add(Convert.ToInt32(textbox1.Text));
}
void OnAddIntegers() {
if (integerList.Count==0)
DisplayError();
else {
int sum=0;
for (int i=0;i<integerList.Count;i++)
sum+=integerList[i];
textBox2.Text=sum.ToString();
}
}
|
This short sample illustrates the two important event-handlers: what to do when a button is clicked. As you can see, what needs to be done is different depending on what data has been input so far.
Conclusion At first glance, even-driven programming seems more complex than structured programming, and in a sense this is true. However, an event-driven structure eases complexity by allowing you to program in the context of what the user is doing. If the problem can be thought of in the user's terms, that can make programming it a much more satisfying, and less-error prone process.
|
|