A Crash Course in User Interfaces
Whenever the topic of interface development comes up, I’m always surprised to see most software engineers cringe as if they’re being told they need a root canal. Almost all modern applications require some sort of graphical user interface, and yet the UI is commonly the last consideration of development. Worse yet (particularly when it comes to web development) the user interface is often created by a graphic designer who isn’t familiar with software development. The resulting separation that occurs between the application’s internals and its interface can cause serious problems with the project.
The Black Magic of Interface Development
This aversion by software engineers to interface development is unfortunate, and can endanger the success of a software project. Since the UI is the only area of the application that the user interacts with, to them, there generally is no difference between the concept of the interface and the concept of the application. Internally, the software may be well thought out, extensively commented, using the most cutting-edge technologies and practices available — but if you make Aunt Marge enter commands at a text prompt, she’s not going to care. She’s going to think your application sucks. And she might hit you with her cane.
Many software developers look at interface design as a type of voodoo, except that all that sticking pins into your monitor is going to accomplish is leaking liquid crystal. Some of this is undoubtedly a result of the lack of formal training on event-based programming in software engineering curricula. During my entire time in college, only a single class contained any projects requiring a real event-driven graphical user interface. Then again, my education was based in C++, and I appreciate the powers-that-be not subjecting the students to the bone-crushing, mind-warping horrors of MFC. Maybe this is one of the (very few) benefits of moving university-level curricula to Java. :)
Simple Structured Interfaces
The secret? Interfaces aren’t nearly as complex or foreign as many developers make them out to be. In my experience with them, I’ve gradually converged on some general guidelines which I’ll share with you in this article. Interfaces can be easily separated into a small amount of “states”, which represent the different situations that the interface can be in. Different conditions in the application can cause the interface to move between states. This idea is represented in computer science as a finite state machine. For those readers with a strong theoretical background or a masochistic personality, I suggest reading the Wikipedia article for more information. A basic understanding of multi-threading is also assumed from here on, but don’t worry, I’ll be gentle.
If reading about complex abstract theories isn’t your idea of fun, don’t worry. (This just means you’re a software engineer, not a computer scientist. This is not a bad thing.) It all starts with the state diagram below, which illustrates the basic structure of an event-driven user interface. (Click the diagram for a larger version.) If you’re not used to state diagrams, or even the idea of application states, don’t be intimidated; I’ll walk through each step in more detail. At the end of the article, you’ll be able to impress your friends by saying the phrase “finite state machine” at parties. (Note: any swirlies you receive as a result are not the responsibility of the author.)
The circles in the diagram indicate the various states of the application’s interface. The arrows connecting them indicate ways to move between states. Roughly, entering a state means that a specific method inside the application will be called. Arrows in the diagram indicate pathways to move between events. Arrows with labels indicate an event must occur in the application for the state to change, whereas arrows without labels indicate that the state change will occur naturally once the work in the current state finishes. Often, the labeled arrows represent a collection of similar events that can occur to move the interface between states — for example, a collection of button click event handlers.
In an event-driven programming model, the firing of events essentially result in a certain method or collection of methods being called. In a .NET application, this method will match the signature of a delegate, which will be defined as an event handler. (For a solid introduction to .NET delegates and events, see here.) For the web developers among us, events are generally handled by a JavaScript function, which are designated as event handlers through HTML attributes like onclick.
The Start State
As you may have guessed, the application begins in the Start state. As it starts up, it creates the components of the user interface during the construction step, which moves the interface to the Initialize state. Whatever preliminary work must be done to make the interface display its default view is done in this state — for example, the application must load information into list boxes, and apply any user preferences that may exist. In “thick” applications (rich interfaces), this is often done inside a constructor. In “thin” applications (web interfaces), this is done during the HTML rendering step when the page is loaded.
The Idle State and the Lazy Ninja Interface
Once the interface has completed initialization, it enters the Idle state. This is where it will spend the majority of its time, simply waiting to respond to incoming events. The loop in the diagram pointing directly back to the Idle state indicates that the application will remain in the state by default, and only move to another state when an event occurs. In a rich environment (either a thick client, or an AJAX-enabled thin client), the application has the ability to process external information while the interface is considered idle. This responsiveness results from the power of multi-threading, but a deeper discussion of concurrency is beyond the scope of this article. Incidentally, the lack of this additional thread is why “old-style” web applications without the magic of AJAX generally require user interaction (say, a page refresh, or an HTML form submission) to update the interface.
An important guideline when developing interfaces is that you want to keep the interface in the Idle state as much as possible. The more time the thread controlling the interface is idling, the more responsive your interface will be, and the more rich of an experience will be provided to the user. If you’re into anthropomorphizing inanimate objects (for example, you’ve been known to talk to plants), you can think of the interface as being lazy. It doesn’t want to do any work itself. Instead, it just wants to know when it needs to pass the work on to another segment of the application. When a request is received, the interface has to immediately get motivated with ninja-like speed and dispatch it to someone who can handle it. Otherwise, your job as a developer is to give your lazy ninja interface a bag of pork rinds and a nice comfy spot on the couch.
The Feedback State
There are three events that will move the interface out of the Idle state. The first possibility is that the user will interact with the interface, such as pressing a key or moving or clicking their mouse. This moves the interface into the Feedback state. In this state, the interface provides the user with graphical and/or audible feedback to indicate that it has understood what the user is trying to tell it. For example, the interface may change the color of a button and emit a “click” noise when a user clicks a button. From a psychological perspective, providing constant positive feedback helps to keep a user engaged in the application, and improves their overall experience significantly.
The Response State
After the user has been presented with feedback to indicate that the interface has received the user’s command, it immediately moves into the Response state. The task during this state is to fulfill whatever request the user has made of the software. In a rich environment, this generally means dispatching the request to a “worker” thread, which will do the necessary processing in the background. This means that the thread controlling the user interface won’t be tied up doing processing, and can remain responsive to provide the user with useful gizmos like progress bars and cancel buttons.
The Synchronize State
Once the user’s request has been fulfilled (or at least dispatched to a worker thread), the interface needs to re-assess the situation. What has changed in the environment, and how should it affect the tools presented to the user? This is the state where buttons are be disabled (”grayed out”), status messages are updated, data that has changed is re-loaded into the interface, and anything else that has to be accomplished in order to bring the interface into sync with the status of the application is done.
Another way the interface can enter the Synchronize state is if it detects an external event. Usually, these will be fired from a background thread that is monitoring some external conditions, and when these conditions change, an event will fire indicating that the interface should be updated to reflect the updated information. For example, in a chat application, there is a thread in the background that listens for the person on the other end to transmit a message. When that message is received, the background thread fires an event that tells the interface “Quit sitting around! Draw this new text on the screen!” The interface will jump directly into the Synchronize state and update itself with whatever new information is available, so the user is immediately aware that pR1Nc3ssSuzi3 has just sent them a message saying “omg lol kthx bye”.
Once the interface has synchronized itself with the current application state, it immediately grabs its bag of pork rinds and re-enters the Idle state, where it will wait for the next event it has to respond to.
The Cleanup State
The final way that the interface can leave the Idle state is when it is “disposed”. This occurs when the application is shutting down, or the interface is no longer needed (for example, when a chat window is closed). In this state, the interface should do whatever necessary to clean itself up and prepare for destruction. This is where the interface should “un-subscribe” from any events that it is listening to if the language you’re using requires it. In this state, you should also make sure that no work is being done in the background, and if so, you should either wait for it to finish or let the user decide to cancel it.
The Stop State
This state is reached when the components that compose the interface are destroyed in memory. Obviously, since the interface no longer exists, it doesn’t do anything in this state. Unless it comes back to life as a zombie interface and starts eating RAM like real-life zombies (?) eat brains, but that’s a different topic for a different day. (Actually, your interface can become “zombified” in .NET if you interop with native COM components without cleaning them up correctly. Be sure to free them while you’re in the Cleanup state.)
Summary
In this article, we’ve talked about voodoo, lazy ninjas eating pork rinds, real-life zombies, swirlies, and also user interfaces. A few things to keep in mind:
- Keep your interfaces lazy, but give them ninja-like reflexes. Accomplish this by not executing long-running processes in the interface thread.
- Each state in the interface won’t be represented by a single function in your code. In particular, in strong-typed languages like .NET ones, you’ll need more than one event handler.
- However, try to make your code cohesive (and not repetitive) by keeping business logic out of the event handlers themselves. Create a small number of methods that will do the work required, and call these methods from your event handlers. This way, if you add a button that does the same thing as a menu item, for example, both event handlers can call the same method without repeating the actual logic twice.
- Provide your users with constant and consistent feedback to keep them engaged in your application. No, this does not mean you are allowed to use the
<marquee>tag. Progress bars and cancel buttons are great, though. - Interfaces are not black magic. You can create good ones without being a voodoo practitioner. All it takes is some patience and a little bit of experience.









[...] crap. In accordance with good interface design strategies, the Excel export feature is done in the background using a ThreadPool worker thread. Using the [...]
Hey Nate,
The state diagram to which you have given the link the in the above post is not to be found. Can you please fix this. By the way, lovely post.
Cheers,
Afif
Ok, so I found your blog.. I don’t remember how but my browser does. It just so happens that there is a certain ancient UI that needs rewriting. I am trying to figure out a technology to use. .NET is out since it has to work under X-windows too. Reading your site inspired me to create my own site. I rambled on about it on my shiny new site. Perhaps you could comment on the post there — if you can be pried away from the Wii that is!