Note: The following chapter is a preview excerpt from O'Reilly's Essential ActionScript 2.0 by Colin Moock. You can purchase the book on the O'Reilly website.
In Chapter 18, Colin explains the concept of the Model-View-Controller (MVC) design pattern, a leading architectural approach for software application planning, and how to use ActionScript 2.0 to implement it in Flash applications. After explaining the MVC concept, he offers a sample application project: a clock with both digital and analog time displays. We've excerpted the introduction below. To construct the clock sample application, download the complete chapter. (PDF, 228K).
While Flash MX Professional 2004 is not required for this article, it is recommended:
Note: This chapter is excerpted from Essential ActionScript 2.0. Copyright © 2004 O'Reilly Media, Inc. All rights reserved. This is a preview, beta chapter and may not reflect the final form or content of the printed book.
In the MVC paradigm the user input, the modeling of the external world, and the visual feedback to the user are explicitly separated and handled by three types of object, each specialized for its task.
—from Applications Programming in Smalltalk-80(TM):How to use Model-View-Controller (MVC), by Steve Burbeck, available at: http://st-www.cs.uiuc.edu/users/smarch/st-docs/mvc.html
The Model-View-Controller (MVC) design pattern separates user interface code into three distinct classes:
For example, in a toggle button, the model would store the state of the button (on or off), the view would draw the button on screen, and the controller would set the state of the button in the model (to on or off) when the button is clicked. But an interface need not be visual. In some cases, the view might play a sound or, as a non-ActionScript example, the view might make a video game controller vibrate.
MVC originated in the Smalltalk language and has been used widely for years in many different incarnations. Though the basic principles of the pattern are easy to understand, its details are complex enough to foster an enormous amount of debate and contradictory implementations. In this chapter, we'll study a relatively traditional implementation of the pattern, bearing in mind that there is no single "right" way to implement MVC.
The basic principle of MVC is the separation of responsibilities. In an MVC application, the model class concerns itself only with the application's state and logic. It has no interest in how that state is represented to the user or how user input is received. By contrast, the view class concerns itself only with creating the user interface in response to generic updates it receives from the model. It doesn't care about application logic nor about the processing of input; it just makes sure that the interface reflects the current state of the model. Finally, the controller class is occupied solely with translating user input (provided by the view) into updates that it passes to the model. It doesn't care how the input is received or what the model does with those updates.
Separating the code that governs a user interface into the model, view, and controller classes yields the following benefits:
Despite those benefits, not all user interfaces are best implemented with MVC. For example, in Chapter 12 we built a simple currency converter application using a single class, CurrencyConverter. The conceptual responsibilities of the model, view, and controller were still manifest in that application, but they were encompassed by a single class. The currency converter application was so simple that the cost of implementing formal MVC outweighed the benefits.
TIP: Design patterns offer more benefits to larger projects than to smaller ones, but the concepts in a pattern like MVC can still inform the design of simple applications like the currency converter.
The MVC pattern can be applied to a single user interface element (like a button), to a group of user interface elements (like a control panel), or to an entire application. This chapter uses MVC to create a clock that combines three user interface elements: a digital display, an analog display, and a toolbar for starting, stopping, and resetting the clock.
Before we study our specific clock example, let's explore the general structure of the MVC design pattern.
Although the model, view, and controller classes in MVC are intentionally segregated, they must communicate regularly. The model must send notifications of state changes to the view. The view must register the controller to receive user interface events and, possibly, request data from the model. The controller must update the model and, possibly, update the view in response to user input.
To facilitate this communication, each object in MVC must store a reference to the other object(s) with which it interacts. Specifically, the model instance needs a reference to the view instances that render it, while the view and controller each needs a reference to the model and reciprocal references to each other. Figure 18-1 shows how the objects in MVC reference one another. The diamond shape in the figure represents a composition relationship, in which one object stores an instance of another.
Figure 18-1. Object references in MVC
Communication proceeds in a single direction (as shown in Figure 18-2) through the object references shown in Figure 18-1, as follows:
Figure 18-2 depicts the MVC communication cycle. The starting point for the cycle is typically the receipt of user input. However, another part of the program could also start the cycle by modifying the model directly (perhaps in response to new data arriving from a server).
Figure 18-12. The MVC communication cycle
Note that Figure 18-2 shows the simplest case of MVC, with a single model, a single view, and a single controller. However, it's not uncommon for an application to provide two or more views for a single model. For example, in our MVC clock implementation, we'll use one model to manage the time, but we'll have three views—one to represent the clock in digital format, one to represent the clock in analog format, and one to display buttons that start, stop, and reset the clock. You may wonder how these buttons qualify as a view of the clock given that they do not represent the time. Views do not necessarily have to represent the entire model and need not even represent the same information (the way the analog and digital views do). As we'll see later, the buttons in our clock do not indicate the current time; instead, the button states reflect whether the clock is currently running, which is simply another aspect of the model.
While an MVC implementation may contain multiple views per model, every view has exactly one controller instance and vice versa. Each view's controller is dedicated to that view's sole service.
TIP: The view and the controller form an indivisible pair. MVC requires that each view has a controller (even if it is just the placeholder value, null) and each controller has a view.
Some views do not allow user input. For example, a view might be a simple graph that cannot be edited. When a view accepts no user input, it does not need a controller to translate user input into model updates. Hence, views that do not allow user input have either null in place of a controller or a controller that does nothing in response to user input.
As we've already learned, responsibilities in an MVC implementation are divided among the model, view, and controller. By the end of this chapter, you should be mumbling the MVC mantra to yourself at the grocery store—"the model manages data and logic, the view creates the interface, and the controller processes user input." Those general responsibilities break down into many specific tasks, covered next.
The model stores data in properties and provides application-specific methods
that set and retrieve that data. The data-management methods are not generic;
they are customized per application and must be known to the controller and
the view. For example, the model in our clock application defines methods specific
to a clock, such as setTime( ) and stop( ). Controllers
in MVC are custom written to manipulate a specific model; for example, a controller
in our clock application must know about the setTime( ) and stop(
) methods in order to modify the model.
The model's data can be changed externally by an outside class or internally by its own logic. For example, the time of our clock's model might be reset by the controller in response to user input, or it might be updated because, internally, it detects the passing of a second. (It might be monitoring the operating system's built-in clock or, as in our upcoming example, tracking the passing milliseconds itself.)
The model must also provide a way for views to register and unregister themselves, and it must manage a list of registered views. Whenever the model determines that its state has changed meaningfully, it must notify all registered views.
Finally, the model implements the logic of the MVC triad. For example, the
model in our clock application implements a tick( ) method that runs once per second, updating the time. The model might also provide data validation services and other application-specific utilities, such as loading the current time from a server-side application.
The view must create the user interface and keep it up-to-date. The view listens for state changes in the model; when the model changes, the view updates the interface to reflect the change. For example, in our clock application, when the time changes, the model notifies the three registered views. In response, the analog clock view positions the hands of a traditional clock, while the digital clock view sets the numbers in its digital display. The third, "buttons" view
(ClockTools) changes the appearance of the buttons to indicate whether the clock is running or has been stopped.
Each view must forward all input events to its controller. It should not process any inputs itself. For example, our analog clock view might allow the user to set the time by dragging the hands of the clock. When the hands are dragged, the view merely forwards the input information to the controller, and the controller decides what to do.
Depending on the specific implementation, the view might query the model for
its state in order to determine what changed when an update is received. The
view never changes the model but can retrieve information from it. For example,
our clock's model might tell its views that the time changed, and the views,
in response, might invoke getTime( ) on the model to determine the new time. Alternatively (and more commonly), the model might send the new time to the views directly in an info object at update time. You should recognize these two options as the push and pull models discussed in Chapter 16 for the Observer pattern. See that chapter for details.
You may be wondering whether the MVC design pattern as presented makes sense architecturally. It may seem odd that, if the user changes something in the view, instead of responding immediately, the view passes the input to the controller, which passes it to the model, which notifies the view of the change, and finally the view requests the details of the change from the model and renders them for the user. Wouldn't it be easier to skip all the detailed communication and just have the view update itself whenever the user makes a change? In some sense that may be easier, and in a simple application it might be appropriate. As an application becomes more complex, however, the MVC pattern offers significant benefits. For example, in the case of our clock implementation, in which there are multiple views, the architecture allows changes in one view to be detected by and reflected in all views. Therefore, you don't have to write code to make the analog clock view notify or update the digital clock view (or vice versa). Furthermore, the logic is centralized in the model, which prevents code duplication and allows us to add or remove views at runtime with minimal effort. For example, you could add a view that displays time in 24-hour (a.k.a. military) format instead of 12-hour (a.m./p.m.) format. Complex applications demand this type of flexibility, and MVC provides the structure to implement it.
The controller listens for notifications from the view based on user input and translates that input into changes in the model. In some cases, the controller makes logical decisions about the input before making a corresponding change to the model. For example, our clock application has a Reset button that resets the time to midnight. When the button is clicked, the clock controller translates the input conceptually from "Reset button clicked" to the command "Set model's time to 00:00:00."
In some special cases, the controller might also instruct the view to make changes to the user interface by calling methods on the view. The changes are sent directly to the view only when they are purely cosmetic and have no effect on the model. For example, if a user interface has buttons to alphabetize a list of names in ascending and descending order, the controller may legitimately call the appropriate sort methods on the view when those buttons are clicked. Calling the sort methods is legitimate because it does not change the underlying data stored in the model (i.e., the list of names); only the presentation of that data changes.
Through all this talk of the model, view, and controller, we still haven't
seen how to make instances of those classes. That's partly because there's
no single, definitive way to instantiate the classes in MVC. In our clock example,
we'll create a single class, Clock,
which instantiates the model, its views, and their controllers. Our Clock class sets up the MVC classes as follows:
Notice that the controllers are missing from the preceding list because they are created by their respective views.
Hence, our Clock class forms a wrapper around the MVC triad, packaging it into a tidy, self-contained unit. However, that's definitely not the only approach possible. At least one Java implementation suggests that the controller class should create the model, and possibly the view, in its constructor!
In our example code, we'll follow the traditional (Smalltalk) implementation of MVC, in which the model and view(s) are created by some containing class, and then the model and controller are registered for each view. When the controller for a view is not specified, the view class creates a default controller for itself automatically.
Note: Download the PDF of Colin's complete chapter to learn more about the Model-View-Controller design pattern and to build an MVC application, a digital/analog clock.
Colin Moock is the author of five best-selling books on ActionScript, all published by O'Reilly Media, Inc. Moock's works include ActionScript: The Definitive Guide, ActionScript: The Definitive Guide for Flash MX, Essential ActionScript 2.0, and Essential ActionScript 3.0. Moock is a regular speaker at international Flash industry conferences, including FITC, Flash Forward, and Adobe MAX.