This excerpt is from Programming Flex 3. If you want to try your hand at developing rich Internet applications with Adobe's Flex 3, and already have experience with frameworks such as .NET or Java, this is the ideal book to get you started. Programming Flex 3 gives you a solid understanding of Flex 3's core concepts, and valuable insight into how, why, and when to use specific Flex features. Learn to get the most from this amazing and sophisticated technology.
Table of Contents
Throughout this book, you’ve learned about all the pieces comprising Flex. But you haven’t seen how these pieces come together to create a working application. An academic knowledge of Flex is essential to successfully building Flex applications, but that knowledge is of little value unless you also have a concrete understanding of how to use what you’ve learned in practice. In this chapter, we’ll look at how to build a Flex application using everything you learned in the first 21 chapters of the book. We’ll walk through building a sample application to illustrate important concepts, and to give you practical experience building a Flex application.
In this chapter, we’ll take a look at a sample application that illustrates the key points we discuss in the chapter. The sample application is a simple photo viewer that uses the Flickr API from the popular photo-sharing service, Flickr (http://www.flickr.com), to allow users to search for photos via keywords and then view photo details. We call this application FlickrFlex.
Figure 22.1, “The home page of the FlickrFlex application” shows the application’s home page, which displays a random photo and a search form.
The entire application runs in a web browser, and it supports the browser’s back and forward buttons as well as deep linking directly to a search result or to a specific photo’s details.
Throughout this chapter, we’ll take a look at key parts of the application. What we will not do is go line by line through the code and build the application step by step. Instead, we highlight what we think are important concepts, and then we are leaving it to you, the reader, to download the sample application and review the code in more detail. You may also find that you will gain the most from this chapter if you read it more than once.
Once the user searches for photos, the results are shown in a tile grid (see Figure 22.2, “The results of a photo search, shown in a tile grid”).
The user can click a thumbnail to view the photo details screen, as shown in Figure 22.3, “The photo details screen”.
Even though FlickrFlex is a relatively simple application, we think it is complex enough that it encompasses many of the common requirements of Flex web applications. For example, FlickrFlex has to solve problems related to making requests to remote services, which is a very common problem for Flex applications. Also, FlickrFlex has to solve the problems related to browser integration, which is quite a common requirement for Flex applications. By looking at FlickrFlex, we think you’ll be able to see ways to solve common problems that you are likely to find in many of your own projects. The purpose of our discussion of FlickrFlex is to look at larger themes and patterns; strategies for solving common application design problems. Therefore, rather than building the entire application one line of code at a time, we’ll highlight the important features and patterns.
Every project starts with the basics. You always have to configure the environment, get access to services, and create projects files. In the next few sections, we’ll look at the steps required to get started.
The sample application runs off the Flickr service. To follow along you will need to have a Flickr account and an API key. Both are free.
To apply for a Flickr account, go to http://www.flickr.com/signup. Flickr is owned by Yahoo!, and a Flickr account is synonymous with a Yahoo! account. If you already have a Yahoo! account, you don’t need to apply for a Flickr account because you can use your existing account.
Once you have logged in with the Flickr account, go to http://www.flickr.com/services/api/keys/apply to apply for a new Flickr API key. The Flickr API requires the API key. To follow along with this sample application, you should apply for a noncommercial key.
Once you’ve created a Flickr API key, you can continue to the next section. You’ll next get the source code for the application, and you’ll need to edit a configuration file with your Flickr API key.
Rather than trying to walk you through the steps for writing all the code for the sample application, we’ve opted to simply provide you with complete, working source code. You can then spend less time typing code from listings in the book and more time reading about why the code is written the way it is.
To distribute the source code for the sample application,
we’ve created a Google Code project for FlickrFlex. You can view the project site at
http://code.google.com/p/flickrflex/. From the Google
Code project site, you can download the application source code in
one of two ways: using Subversion or as a .zip file.
We recommend using Subversion to download the source code. You can use any Subversion client to check out the project by using the following repository URL: http://flickrflex.googlecode.com/svn/trunk/flickrflex-read-only. This URL will work only with a Subversion client, and not in a web browser.
If you're not comfortable using Subversion (though we highly
recommend that you learn how to use it), you can instead download
the FlickrFlex application as a .zip
file from the project’s download page, at http://code.google.com/p/flickrflex/downloads/list.
Regardless of how you download the project files, we assume that you have downloaded (and extracted, if necessary) all the project files before continuing with the rest of the chapter. Although you can read this chapter and learn much from it without downloading and using the project files, it is likely that you will gain much more if you do follow along with the project files.
Subversion is a version control system. Using a Subversion client and the preceding URL you can download a read-only version of the FlickrFlex project from the Google Code Subversion server. A detailed description of what Subversion is and how to use it is beyond the scope of this book. However, if you want to learn more about Subversion you can read the entirely free Subversion book, Version Control with Subversion, at http://svnbook.red-bean.com. You can also find a list of Subversion client programs at http://subversion.tigris.org/links.html#clients. Using most of these clients is rather simple, especially when checking out a read-only version of a project, and you can generally find instructions for how to check out a project in the documentation for each client.
If you are using Flex Builder 3, you can simply import the Flex
project, as we’ve included all the Flex Builder project files
along with the source code. If you are not using Flex Builder, you
will need to compile the application using mxmlc, as you learned how to do in
Chapter 2, Building Applications with the Flex
Framework. Flickr.mxml is the
main application .mxml file for
FlickrFlex, and TestRunner.mxml is
the main .mxml file for running the
unit tests for the project.
The code for the FlickrFlex application is
complete and working exactly as downloaded. However, you will need to
provide your Flickr API key
and secret, both of which you can find at http://www.flickr.com/services/api/keys/. You should
locate and edit the assets_embed/xml/flickrconfig.xml file in the
application project files. The file contains the following:
<configuration>
<apiKey>Your API key</apiKey>
<secret>Your API secret</secret>
</configuration>
You should replace your API key and your API secret text with the correct values from the Flickr page.
Now that you’ve downloaded and configured the FlickrFlex
project, the next step is to compile it. This is a
straightforward step since there is nothing particularly unusual
about the project. If you are using Flex Builder and you’ve
imported the Flex project, you’ll probably note that two of
the .mxml files are set to be
compilable: TestRunner.mxml and Flick.mxml. The Flickr.mxml file is the file you want to compile,
and in Flex Builder the compilation step is automatic unless
you’ve disabled automatic builds for the project. That means
all you need to do is run the Flickr.mxml file to compile and launch it.
If you’re using the Flex SDK without Flex Builder, you
will need to compile Flickr.mxml
using mxmlc. FlickrFlex does not
require any additional libraries or any unusual compiler
options.
When you have successfully followed the preceding steps, you should be able to compile and run the application. You’ll know the application is working correctly when you can verify the following user stories:
The user sees a random “nature” photo on the home page.
The user can enter a search term on the home page and request search results.
When search results are returned, the user sees thumbnails and titles for all search results displayed in a scrollable grid.
When the user clicks on a thumbnail in the search results, she is taken to that photo’s details screen in which she sees a large version of the photo along with the title and description (if any exist).
From the details screen the user can click a button to return to the previous screen.
From the details screen the user can click Next and Previous buttons to navigate through details of the search results.
The user can navigate back and forward between viewed pages/screens by using the back and forward web browser buttons.
The user can copy the address from the browser address bar at any point and paste it into another browser window to see the same page/screen (a process known as deep linking).
|
There are lots of ways build Flex applications. This shouldn’t surprise you; there are lots of ways to do most things in life, and building Flex applications is no different. Furthermore, it’s impossible to say there is exactly one right way to build Flex applications, just as it is impossible to say there is just one right way to do anything else. However, you can employ certain strategies that increase your likelihood of success when building Flex applications. We call these best practices, and in the following sections we’ll go into a bit more detail regarding a few of the best practices we consider most important. We strongly encourage you to try these out for yourself. Give them a fair chance; you may later discard some of them, but chances are that more than a few will prove to help you and your teams in the long run.
How you organize project files can have a tremendous impact of the success of a project. File organization might seem unimportant, but don’t underrate how influential this factor can be. Organizing files well ensures that not only is it easier to find the files and easier for teams to work together, but also you avoid potential naming conflicts and can facilitate source code versioning (see the section called “Versioning”” later in this chapter).
If you use Flex Builder to create Flex
projects, you have probably already noticed that Flex Builder
provides a degree of organization. Primarily, Flex Builder
distinguishes between where it stores source code (.mxml and .as
files) and where it stores deployable application files
(.swf, .html, and runtime assets). The default names for
the directories that Flex Builder uses are src for source code and bin-debug for deployable application files. This
high-level structure is important and essential to good project
file organization. However, you can do more, and indeed more is
required to keep files organized clearly. The following are a few
of the key things you can do to organize files:
Place all .mxml and .as files (except for the main application
.mxml file) in packages. As you’ve learned, Flex treats
.mxml files as ActionScript classes,
and therefore we can think of them and organize them just like we
would ActionScript classes. We generally recommend using
reverse-domain names for packages. For example, all the classes and
.mxml files for the sample
application in this chapter are organized within a com.oreilly package. After the reverse-domain
package, we advise using a project subpackage
where appropriate. In the sample application, we place all the
files in a pf3 project subpackage.
Then it is considered a best practice to further organize files
into subpackages. For the sample application, we use the following
subpackages:
controllersSee the section called “Abstracting Common Patterns”” later in this chapter for more information about controllers.
eventsWe use this package for all event types (subclasses of
Event).
modelsSee the section called “Abstracting Common Patterns”” for more information about models.
servicesThis package organizes all the business delegate and related classes.
viewsSee the section called “Abstracting Common Patterns”” for more information about views.
Place all embedded assets in a
directory inside the source files directory. In the sample
application, we call this directory assets_embed. This directory can contain
.css files, embedded .swf files, embedded image files, and generally
any sort of file that might be embedded in the application (as
opposed to loaded at runtime). Depending on the number of files, it
may make sense to create further subdirectories to better organize
everything within the embedded assets directory.
Any runtime assets that must be deployed with the application
should be organized within the deployable application directory
(bin-debug). These files might
include .xml files, image files, or
any sort of file that the application loads at runtime. Generally,
these files should be organized within subdirectories within the
deployable application directory.
Flex provides a lot of ways to apply styles to applications. You learned about all of these ways to style applications in Chapter 8, Customizing Application Appearance. This provides a great deal of flexibility in how to style an application, which is both good and bad. It is good in that it provides lots of options. It is bad in that it allows developers and teams of developers to apply styles without a consistent approach or plan.
We find that in general, it is best to apply most, if not all,
styles via external .css files. We
make exceptions for some layout-related styles such as verticalAlign and horizontalAlign, which we frequently apply
inline. However, we prefer to apply the vast majority of styles via
external .css files. For the Flickr
application, we use only one .css
file because the application simply isn’t complex enough to
warrant more than one .css file.
If you look at the FlickrFlex.css
file, you’ll notice that we’ve organized our selectors
to make them somewhat easy to locate within the file. We place the
Application type selector first in the
file, followed by the rest of the type selectors in alphabetical
order. Then we follow the type selectors with the class selectors,
also organized in alphabetical order.
Although it is sometimes tempting to apply styles via inline
attributes to MXML tags, we’ve found it is far better in the
long run to take the extra minute or two to apply styles via
external .css. This has several
advantages, some of which are as follows:
External .css cleans up
style-related clutter from .mxml
files.
External .css allows non-Flex
developers to work on styling an application.
External .css helps to
differentiate between the layout (view/.mxml) and style (.css).
Application components are
invaluable and cannot be overrated. Large .mxml files or ActionScript classes without
clearly defined roles and responsibilities are a good sign that
refactoring is necessary. When an application consists of many
smaller components, it is often easier to work as a team, to debug
an application, and to add new features or make changes to existing
features. Too few components, or components that are too large and
too complex, make applications difficult to manage. It is generally
advisable to err on the side of too many components with
responsibilities that are too granular than the other way around.
As a rule of thumb, all components should have no more than two or
possibly three key responsibilities. If a component has more
responsibilities, it should be refactored into several more
specialized components.
As we’ll see later in this chapter, we use application components extensively for the parts of the application that we call the views and the controllers. Almost universally, developers use application components for views. Our use of application components for controllers is somewhat less usual, but we think it provides a solution to issues that are unique to Flex. You can read more about views and controllers later in this chapter. You can read more about application components in general in Chapter 9, Application Components.
Although versioning may seem like a related practices rather than a core practice, it is nevertheless a core practice that every team should be following for Flex projects. A variety of versioning software tools are available. Some of the most popular are Subversion (which we are using for the sample application), CVS, Mercurial, Git, Team Foundation Server, and Visual SourceSafe (VSS). Every versioning software works slightly differently, and different teams have different philosophies regarding how to manage code repositories. For example, some teams prefer not to add large assets (e.g., videos) to version control repositories, whereas other teams prefer the simplicity of adding everything to one location. What is most important is that you always add all source code to a repository, and you should always keep the repository up-to-date with changes. For the FlickrFlex project and many of our own projects, we like to add everything to the Subversion repository, including source code, Flex Builder project files, runtime assets, and compile time assets (embedded images, fonts, etc.).
Unit testing involves writing code that runs automated tests on project code. A test will run an assertion that will either pass or fail. A group of tests is known as a suite of tests. When running a suite of tests, you can quickly determine whether code is behaving as expected. If all the tests in a suite pass, it indicates that there is a good chance the application is working correctly. If one or more tests fail, it shows you where you should look to correct a problem with the code. Automated tests allow you to run suites of tests frequently, often every time you make a change to the code for an application. The results of the test will help you immediately identify any problems which new code might introduce.
Unit testing does not require that you use a testing framework. However, using an existing testing framework can greatly simplify writing and running test suites. There are two testing frameworks that we know of for use with Flex: FlexUnit (http://code.google.com/p/as3flexunitlib/) and AsUnit (http://www.asunit.org). We find that FlexUnit is a little easier to use with Flex than AsUnit.
If you’re not familiar with unit testing or FlexUnit, you might find the article at http://www.adobe.com/devnet/flex/articles/unit_testing.html to be a helpful introduction.
Once you’ve starting writing unit tests, you’re likely to ask yourself exactly what you should be testing. Although it might be possible to write tests for every part of a Flex application, we find that it is generally easiest and most effective to write tests for only certain parts of an application. In particular, we find that tests for the following are most useful:
For example, if a class has a method to deserialize from XML data, it can be very useful to write a test or tests for that.
These are points of integration that represent relatively high fragility. If the contract with the service changes (e.g., the publisher of the service changes the API), it’s possible that the entire Flex application will stop working correctly. Having good unit tests for business delegates helps you to quickly identify where an error is occurring if a service contract changes or if a service simply goes offline or throws an error.
Any method of a utility class should generally have unit tests.
There is a philosophical approach to software development, known as test-driven development (TDD). This methodology advocates writing tests before writing application code. You then write the application code to get it to pass the tests. Some Flex developers find TDD works well for certain types of classes, such as model classes and business delegates. Other Flex developers don’t find that TDD is well suited to Flex development. We are mixed on the issue of TDD for Flex development, with one of us liking it and the other not. You’ll have to be the judge for yourself.
You can learn much more about test-driven development in the excellent book, Test Driven Development by Example, by Kent Beck (Addison-Wesley).
The sample application has a suite of tests that are included in
the com.oreilly.pf3.flickr.test
package, and the application also includes a test runner,
TestRunner.mxml, to run the tests.
You can see that there are two classes in the test package:
PhotoTest and FlickrServiceTest. The PhotoTest case runs tests verifying that a
Photo object correctly deserializes
XML data as it is returned by the Flickr service. From a TDD
perspective, it is useful to write this test before writing the
Photo class and its deserialization
method. Having the test written first can help to verify that
Photo does what it is intended to do
and that it meets all the requirements placed on it as you write
it. However, even after the class has been written, the tests are
useful because you can run them to verify that no regressions have
occurred as you refactor the application. Likewise, it can be
helpful to write the FlickrServiceTest
class before writing FlickrService,
and yet it is also useful after writing FlickrService to verify that the service works as
intended.
Even with the best practices mentioned in the preceding section, the idea of building a Flex application from scratch can be a bit daunting. Where should you begin? Sometimes too much freedom can be paralyzing. Artists often report that their greatest creativity stems from having boundaries. Likewise, you may find that having structure may provide the right environment for building a successful Flex application. This is the role of a microarchitecture.
Microarchitectures are intended to provide a general guideline and structure for building applications. A microarchitecture doesn’t deal with the specific requirements of a specific application, but rather concerns itself with a philosophical approach to how applications should be structured and designed in general. Microarchitectures provide these oft-needed boundaries and guidelines that help remove the paralysis you might otherwise experience. To that end they can be useful.
Various individuals and organizations promote many microarchitectures for Flex applications. A few of the more popular of these are as follows:
Cairngorm (http://labs.adobe.com/wiki/index.php/Cairngorm)
PureMVC (http://www.puremvc.org)
We don’t advocate the use of any one microarchitecture over another. In fact, we don’t really advocate the use of microarchitectures at all. Instead, we recommend that you find a system that works best for you. For the sample application, we are borrowing some of what we consider to be the best ideas from our own experiences building applications with Flex.
Although any one of these microarchitectures might be helpful and useful, don’t be surprised if it is limiting at the same time. The boundaries created by a microarchitecture can provide structure, but they are still boundaries. If you decide to use one of these microarchitectures, it is probably a good idea to follow the rules and build applications exactly as specified by the microarchitecture until you are familiar with the basics and have built several applications successfully. However, at that time you may find it useful to break the rules a little and adapt them to what works best for you. To really take our advice on this matter you’ll probably need to be fairly proficient with Flex and with basic design and architectural patterns before you even start working with a microarchitecture. That’s not because we think microarchitectures are “advanced” or “difficult.” Rather, it’s because we think that to be able to have a critical approach to using a microarchitecture (such that you can learn from the patterns without having to adopt the entire system blindly) you need to have a fair amount of Flex and application development experience first. For the sample application in this chapter, you’ll find that we’re not stressing that you must build your own applications using the same design structure we’ve used. Instead, we’re simply providing one example that uses patterns that we’ve found to be useful for Flex applications.
|
Like all software, Flex applications are complex. This requires that developers building Flex applications are able to solve architectural and design problems rather than simply throwing together a bunch of off-the-shelf components. Using a microarchitecture as described in the preceding section can help in this regard. However, when using a microarchitecture you can solve only the problems that the microarchitecture is specifically designed to address. If a problem falls outside that scope, the problem remains unsolved. In this manner, it’s perhaps more useful to at least understand some of the common patterns that are used to solve problems. You can then utilize these patterns either as part of or apart from a microarchitecture.
Many papers, articles, and books have been written about design and architectural patterns used in software development. The patterns that you could use when building a Flex application are too numerous to list and detail in this chapter. However, a few patterns are common enough that we would be remiss not to describe them. Notably, nearly every Flex application could profit from the use of the Model-View-Controller pattern and the Business Delegate pattern, both of which we’ll discuss in more detail in the following sections. Additionally, we’ll start by looking at a pattern for dealing with browser integration, which is a problem that is unique to web-based applications.
Shortly we’ll look at much of the code
in more detail. However, before we do so, you’ll need to
understand how the FlickrFlex application handles browser
integration (deep linking and the back and forward buttons). The
principle is rather simple: all significant state change requests
are routed through the BrowserManager class by updating the address
fragment. For example, when the user clicks on a Search button, the
FlickrFlex application does not directly change the state to the
search results screen or immediately make the request to the search
method on the Flickr service. Instead, the application updates the
address fragment via BrowserManager
and waits for BrowserManager to
dispatch an event notifying the application to update as necessary.
We’ll see a few specific examples of this later on. However,
the basic way in which the application manages this behavior
depends on a simple infrastructure provided by a few methods in a
few of the .mxml files, which
we’ll look at now.
The main application .mxml file is
Flickr.mxml. This file contains
little more than a Style tag and an
instance of FlickrController. The
FlickrController class is where we
start to set up browser integration. Within the initializeHandler() method, we tell the
application to listen for changes to the URL via the BrowserManager:
_browserManager.addEventListener(BrowserChangeEvent.APPLICATION_URL_CHANGE,
urlChangeHandler);
_browserManager.addEventListener(BrowserChangeEvent.BROWSER_URL_CHANGE,
urlChangeHandler);
When the URL changes, the first thing we do is parse the fragment into an array, using a forward slash as a delimiter:
var fragments:Array = _browserManager.fragment.split("/");
We then remove the first element from the array and use that to
determine what state to set on the view. If, for example, the fragment is
search/nature (which is what the
fragment would be if the user runs a search using the term
nature), the first element
in the array will be search, which
means we want to tell the view to change to the search screen. In
every case, we pass along the remaining fragment to the
view:
var topFragment:String = fragments.shift();
if(topFragment == "search") {
view.setMode(FlickrView.MODE_SEARCH_SCREEN, fragments.join("/"));
}
else if(topFragment == "") {
view.setMode(FlickrView.MODE_HOME, fragments.join("/"));
}
else if(topFragment == "details") {
view.setMode(FlickrView.MODE_PHOTO_DETAILS, fragments.join("/"));
}
Within FlickrView’s
setMode() method, we change the state
and then assign the remaining fragment to the screenFragment property of the corresponding
screen if appropriate:
public function setMode(mode:String, fragment:String = null):void {
currentState = mode;
if(fragment != null && fragment.length > 0) {
if(mode == MODE_SEARCH_SCREEN) {
searchScreen.screenFragment = fragment;
}
else if(mode == MODE_PHOTO_DETAILS) {
photoDetailsScreen.screenFragment = fragment;
}
}
}
If the entire fragment was search/nature, the setMode() method would change the state to the
search screen state and assign nature
to the screenFragment property of
searchScreen. Later in the chapter,
when we talk about views and controllers,
we’ll see how the search screen handles the fragment.
However, at this point the specifics of how fragments are
ultimately utilized in specific views and controllers aren’t
as important as the overall pattern. To summarize and restate: for
FlickrFlex we have a hierarchical structure of objects
(FlickrController contains
FlickrView, FlickrView contains HomeController, etc.). FlickrController listens for URL changes, and when
the fragment changes, FlickrController
starts to trickle down the fragment updates by notifying
FlickrView. FlickrView uses the fragment to decide how to
change state, and it then passes along the remainder of the
fragment to the controller corresponding to the new state. This
pattern could theoretically be continued for as many levels of
hierarchy that exist.
The majority of Flex applications utilize a service of one sort or another, whether the services are SOAP services, REST services, AMF (Remoting) services, or any other sort. Elements of the Flex application need to interact with these services. For example, a user may request from a service a list of states or provinces for a given country, or a user may fill out a form in Flex and then submit the form to a service method.
In a naïve approach, the actual implementation that a Flex
application uses to interact with a service may be exposed directly
to all elements within the Flex application. For example, if a part
of the application needs to query a SOAP service for a list of
states or provinces, it is possible to allow that part of the
application to create an instance of the WebService component with the appropriate
operation, and to bind the results of the operation directly to a
UI component. This may work, but it is a shortsighted solution that
creates fragility.
Instead of the aforementioned solution, you can use a common pattern known as the Business Delegate pattern. A business delegate is a class that serves as a proxy to a remote service, defining an API that the rest of the application can use without having to know anything about the implementation details. That way, the rest of the application only has to know about its contract with the business delegate. If the implementation details change, the rest of the application doesn’t have to change as long as the business delegate API remains the same. For example, an application may initially utilize a set of SOAP web services. Later the services may be migrated over to use Remoting (AMF) instead. If the application doesn’t use a business delegate, many parts of the application may need updating to work with the new services. However, if the application uses a business delegate, only the business delegate needs to change.
FlickrFlex uses the Business Delegate pattern. In this
application, we define a class called com.oreilly.pf3.flickr.services.FlickrService.
This class allows us to define a simple API for the rest of the
FlickrFlex application to use without having to know anything about
the implementation details. The FlickrService class defines only three methods:
searchPhotosByText(), getPhotoDetails(), and getPhotoSizes(). The rest of the application can
call these methods without having to know whether FlickrFlex is
actually making requests to local XML files or a remote service.
However, it turns out that in this example we are going to have the
application connect to a remote service.
In the FlickrFlex application, we are connecting to only one service: the Flickr service. We don’t want to get too bogged down in the details of the Flickr service since our primary focus in this chapter is to better understand common Flex application design and development problems and solutions. However, to make sense of the code for the sample application we’ll need to explain just a few things about working with the Flickr service.
For the purposes of the sample application, we’re necessarily using only a small subset of the Flickr API. We’re calling only three Flickr service methods: one to search photos, one to get information about a specific photo, and one to get the different sizes of a specific photo.
You can always read more about the Flickr API, including all the available methods and their signatures, at http://www.flickr.com/services/api.
The Flickr service uses what is known as representational state transfer,
more commonly known as REST. REST uses URLs to access resources.
The Flickr service allows us to call methods via resources. In the
case of the sample application, there are three methods that we
relate to business delegate methods: flickr.photos.search, flickr.photos.getInfo, and flickr.photos.getSizes. Next we need to look at
how to access these methods.
You must use the same pattern to access any Flickr resources.
You must make an HTTP request to the REST resource along with a
query string indicating the method and parameters. The REST
resource is http://api.flickr.com/services/rest/. Therefore, you can
see that in FlickrService we define a
constant called REST_URL as
follows:
static private const REST_URL:String = "http://api.flickr.com/services/rest/?";
The trailing ? is there to precede
the requisite query string that we will look at next.
The query string for Flickr service requests must always include
the method name along with the required parameters for that method.
The Flickr API documentation lists the required parameters for each
method. All methods require the API key. You can see that in
FlickrService we define three methods
that call REST service methods, each method calling a different
service method with different parameters, but each following the
same basic pattern. We’ll look at searchPhotosByText(). This method calls the
flickr.photos.search method, which
requires a text parameter, which is a
comma-delimited list of words for which to search.
public function searchPhotosByText(text:String):PendingOperation {
text = text.split(" ").join(",");
var method:String = "flickr.photos.search";
var parameters:Array = new Array();
parameters.push(new NameValue("api_key", _apiKey));
parameters.push(new NameValue("method", method));
parameters.push(new NameValue("text", text));
var url:String = REST_URL + createQueryString(parameters);
var pendingOperation:PendingOperation = new PendingOperation(url);
return pendingOperation;
}
You can see that this method creates an array of NameValue objects, which are simply objects with
name and value properties, and then passes that array to
createQueryString() to create the
query string, which it appends to the REST URL.
If we look next at createQueryString(), we can see that it simply
takes all the names and values and strings them together with
= and & delimiters:
private function createQueryString(parameters:Array):String {
var queryString:String = "";
var i:int;
for(i = 0; i < parameters.length; i++) {
queryString += parameters[i].name + "=" + parameters[i].value + "&";
}
return queryString;
}
The searchPhotosByText() method
then returns a new PendingOperation
object, passing it the newly constructed URL. The PendingOperation class simply makes an HTTP
request to the specified URL, and it proxies the response,
dispatching an event when the response is returned.
package com.oreilly.pf3.flickr.services {
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.HTTPStatusEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
public class PendingOperation extends EventDispatcher {
static public const RESULT:String = "result";
static public const ERROR:String = "error";
private var _loader:URLLoader;
private var _vo:*;
public function PendingOperation(url:String, vo:* = null) {
_loader = new URLLoader();
_loader.addEventListener("complete", resultHandler);
_loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, errorHandler);
_loader.load(new URLRequest(url));
_vo = vo;
}
private function resultHandler(event:Event):void {
dispatchEvent(new ResultEvent(RESULT, false, true,
ParsingUtility.parse(new XML(_loader.data), _vo)));
}
private function errorHandler(event:HTTPStatusEvent):void {
dispatchEvent(new FaultEvent(ERROR));
}
}
}
You can see that the PendingOperation class relies on a method of the
ParsingUtility class. We’re
using this custom ParsingUtility class
to determine what type of response the service has returned and
convert it to the correct type. For example, when the PendingOperation object is handling the response
from a flickr.photos.search method,
the ParsingUtility class detects the
response as such and converts the XML into an ArrayCollection of Photo objects, which is exactly what the rest of
the FlickrFlex application expects.
We’ve designed the FlickrService and PendingOperation classes in this way so that the
rest of the application needs to know nothing about the
implementation details. As far as the controllers and views and the
rest of the application are concerned, it makes no difference
whether the Flickr service is a REST service. If Flickr decided to
change to an AMF service at some point, we could adapt the
application with little to no changes to any of the code outside
the services package.
The Model-View-Controller (MVC) architectural pattern is utilized across many languages and platforms. If you have experience building software of any sort, chances are MVC is familiar to you. Volumes have been written about this pattern, and we won’t try to repeat much of what has already been said. Instead, we’re primarily concerned with the usefulness of the pattern in Flex applications and the nuances of how to implement it practically in Flex. However, if you aren’t already familiar with this pattern, we’ll outline it briefly.
The principle concern of MVC is to effectively separate the business logic and the user interface code such that they interact with one another only through well-defined programming interfaces. The idea is that this approach reduces fragility because you can make changes to one without having to necessarily make changes to the other.
As the name implies, this pattern typically uses three parts—model, view, and controller:
The model is the way in which data is represented programmatically.
The view is the user interface. Typically, a view does not directly modify the model it represents. Instead, it allows the controller to modify the model, and the view updates itself when the model changes. Additionally, usually a view does not call methods of its controller, nor does it even have a reference to a controller. Instead, a view usually merely dispatches events that a controller can handle.
The controller is the business logic. A controller typically has a reference to a model and a view, and it uses these references to modify the model and call methods of the view directly.
Although it’s possible to write models, views, and controllers in a 1:1:1 ratio, it’s also possible that a controller could be used with many different views and that a model could likewise be used by many different views and controllers.
You can implement the MVC pattern in many different ways at many different levels. At one level, it is possible to create one model and one controller for an entire application (which uses many different views). It is also possible to implement the pattern at a more granular level, creating controllers, models, and views for all components. How you implement the pattern will depend on the complexity of the application and your preferences as a developer. In the FlickrFlex application, we have opted for a more granular approach.
Next we’ll look at creating models, views, and controllers in Flex applications.
Although there are variations on how to implement a model in a
Flex application, those variations are relatively narrow in scope,
and for the most part Flex developers tend to build models in a very similar fashion. At
the core of the model implementation is something called a
business object (or sometimes more specifically a
value object). A business object is a type that
represents entities (logical groupings of data) within the business
domain (application). A value object is a more specific type of
business object that has no additional logic applied to it, and
merely stores data. In the case of FlickrFlex, we have only one business
object, a class called com.oreilly.pf3.flickr.model.data.Photo.
The Photo class is a fairly standard
implementation. It models the same basic data as is returned by the
various photo-related service methods, defining private properties for all of them:
private var _id:String; private var _owner:String; private var _secret:String; private var _server:String; private var _farm:String; private var _title:String; private var _thumbnailUrl:String; private var _description:String; private var _tags:ArrayCollection; private var _imageMediumUrl:String; private var _imageLargeUrl:String;
The class then defines accessor methods for most of the properties, as in the following:
public function get id():String {
return _id;
}
Also, a few properties can be updated after the initial creation
of the object. Therefore, we create mutator methods as well, and
because we want user interface elements (views) to be able to
update themselves based on the changes, we set the properties as
bindable. The description property is
an example:
[Bindable(event="descriptionChanged")]
public function set description(value:String):void {
_description = value;
dispatchEvent(new Event("descriptionChanged"));
}
public function get description():String {
return _description;
}
Various developers have different perspectives on how a business
object should be created. Some developers are of the opinion that
business objects should not know anything about the manner in which
they are created. Other developers prefer to make business objects
responsible for creating themselves, given specific input. For
Photo, we’ve opted for the
latter approach, creating a static parseFromXml() method that accepts the return XML
from a Flickr photo search, and constructs and returns a
corresponding Photo object:
static public function parseFromXml(xml:XML):Photo {
var id:String = xml.@id;
var owner:String = xml.@owner;
var secret:String = xml.@secret;
var server:String = xml.@server;
var farm:String = xml.@farm;
var title:String = xml.@title;
return new Photo(id, owner, secret, server, farm, title);
}
Frequently, applications require complex models, and it can be
useful to maintain an application-wide reference to the models used
by various parts of the application. For this purpose, a model
locator can be a valuable part of the model. The model locator is
typically implemented using the Singleton design pattern, and it
stores references to various models in use. For the FlickrFlex
application, we’ve defined a model locator called
com.oreilly.pf3.flickr.model.ApplicationModel.
The model locator for FlickrFlex is an application-wide repository for stateful model data, including the following:
The current search term
The current search results
The selected photo
The index of the selected photo in the search results
The implementation of controllers within Flex applications differs among developers much more widely than does the implementation of models. As we indicated earlier, it is possible to build an application that uses one application-wide controller. This is an approach advocated by some microarchitectures such as MVCS. This is certainly a workable approach, and it has its advantages. However, one drawback of this approach is that the one controller is generalized, and the larger and more complex the application becomes the larger and more complex the controller becomes. Therefore, for many applications it is advantageous to use more granular controllers that are specific to components of the application. This is the approach we use with the FlickrFlex application.
Implementing controllers in Flex can be challenging because of the way Flex is designed. Typically, views are user interface elements and controllers are not. As a result, it may initially seem to make sense to have components create and maintain a reference to their own controller. However, this approach has serious drawbacks; most notably it reverses the typical relationship between views and controllers. A controller should provide the programmatic interface to a component, and it should be able to theoretically control any view that implements the interface the controller expects. By having a view construct a controller, these roles and behaviors are no longer possible.
With the FlickrFlex application we’ve taken a slightly
unconventional approach. We’ve decided to define controllers
using MXML, making controllers extend UIComponent. This enables controllers to be added
as children of other containers, and it allows controllers to then
add views to themselves. If you look in Flickr.mxml, you’ll notice that it is very
simple. In this document, we create an instance of FlickrController:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
xmlns:views="com.oreilly.pf3.flickr.views.*" xmlns:controllers="com.oreilly.
pf3.flickr.
controllers.*" backgroundColor="#4F5050">
<mx:Style source="assets_embed/css/FlickrFlex.css"/>
<controllers:FlickrController/>
</mx:Application>
In FlickrController we create only
one component instance: an instance of FlickrView. FlickrController is the control for FlickrView.
Inside FlickrView you can see that in the various states
we create instances of different controllers: HomeController, SearchController, and PhotoDetailsController. In each controller
document we create just one component instance: HomeScreen, SearchScreen, and PhotoDetailsScreen, respectively. If you look at
the code for these views, you’ll notice that they don’t
reference the controllers. Instead, if any communication is
necessary with the controllers, it is achieved by dispatching
events.
Next we’ll look specifically at the SearchScreen and SearchController to better understand how these
two classes are designed and how they work both together and in the
context of the application.
In the com.oreilly.pf3.flickr.views.screens package
you’ll see SearchScreen.mxml,
the view for the search form/search results in the FlickrFlex
application. SearchScreen is fairly
representative of the rest of the views in the application and how
we typically build views in general. The key points to notice about
SearchScreen are as follows:
The view defines an API to grant access to anything related to
its own state, including changes to components within it.
You’ll notice that SearchScreen
defines getters and/or setters for the following: dataProvider, searchTerm, isSearching, and selectedPhoto. We’ll talk more about each in
just a moment.
The view does not have any reference to a controller. You can
see that SearchScreen doesn’t
know anything about SearchController.
However, there are a few instances where SearchScreen needs to communicate with its
controller. This is achieved through dispatching events. You can
see that when the user clicks on an item in the tile list the view
dispatches an event, and it dispatches a different event when the
user clicks on the Search button.
The SearchScreen component, as with
all views, is primarily responsible for user interface layout and
for relaying user interaction to a controller via events. The
SearchScreen consists of a search form
(a text input and a button) and a tile list, as shown in the
following snippet from the code:
<mx:HBox width="100%" height="45" backgroundColor="#212122" verticalAlign="middle">
<mx:HBox styleName="green" height="100%">
<mx:Label text="tags" />
<mx:TextInput id="searchTermInput" text="{_searchTerm}" />
<mx:Spacer width="10" />
<mx:Button id="searchButton" label="Search for Photos"
styleName="green" height="100%"
click="dispatchEvent(new PhotoSearchEvent(searchTermInput.text));" />
</mx:HBox>
</mx:HBox>
<mx:TileList id="photoList" width="100%" height="100%"
itemRenderer="com.oreilly.pf3.flickr.views.renderers.PhotoRenderer"
dataProvider="{_dataProvider}"
change="dispatchEvent(new PhotoSelectEvent(photoList.selectedItem.id));" />
You can see that in the two cases where there is user interactivity (the user clicking on the Search button or selecting an item from the tile list), the view merely dispatches an event.
As mentioned earlier, the view defines an API for reading and
writing values and/or state on the view. One of the key
getters/setters is dataProvider, which
is fairly standard for most views. The dataProvider getter/setter merely gets/sets the
bindable _dataProvider property, which
is an ArrayCollection in the case of
SearchScreen. The tile list is bound
to the _dataProvider property such
that whenever the property changes, the tile list reflects those
changes. We’ll look at when and how this property changes
when we look at the controller code in just a minute.
The other two getters/setters are searchTerm and isSearching. The searchTerm getter/setter is used to get and set
the keyword(s) displayed in the search form, when applicable. The
isSearching getter/setter is used to
change the state to disable the view when a search is running or
when the result is returned from the service.
The selectedPhoto getter is
designed to return a reference to the Photo object corresponding to the selected tile
list item. This provides a well-defined API such that the
controller can access that information without having to know
anything about how the view is implemented.
The SearchController class is
designed to handle all the business logic for searching, displaying
results, and navigating to photo details. The SearchController has an instance of SearchScreen with an ID of view:
<screens:SearchScreen id="view" photoSearch="photoSearchHandler(event);" photoSelect="photoSelectHandler(event);" />
When the photoSearch or
photoSelect event occurs (dispatched
by the view), the controller handles the event. In both cases, the
controller handles the event by updating the browser address
fragment using BrowserManager:
private function photoSearchHandler(event:PhotoSearchEvent):void {
BrowserManager.getInstance().setFragment("search/" + event.searchTerm);
}
private function photoSelectHandler(event:PhotoSelectEvent):void {
BrowserManager.getInstance().setFragment("details/" + event.photoId);
}
You’ll recall from earlier in the chapter that state
changes are often managed by changes to the browser address (via
BrowserManager). You may recall from
that discussion that when changes are detected in the URL,
FlickrController tells FlickrView to change its state, and FlickrView may then pass along part of the address
fragment to the corresponding controller via a screenFragment property. Therefore, when the
fragment is changed to search/search
term, the search term gets assigned to the screenFragment property of SearchController. Therefore, we define a
screenFragment setter as follows:
public function set screenFragment(value:String):void {
value = unescape(value);
ApplicationModel.getInstance().searchTerm = value;
if(_searchTerm != value) {
view.searchTerm = value;
searchForPhotos(value);
}
}
You can see that if the search term is the same as the existing
search term, no further action is necessary. However, if the search
term is different, the controller updates the search term for the
view and runs a method (searchForPhotos()) that uses the FlickrService instance to query the Flickr service
for photos with that search term:
private function searchForPhotos(searchTerm:String):void {
_searchTerm = searchTerm;
view.isSearching = true;
var pendingOperation:PendingOperation = _service.searchPhotosByText(_searchTerm);
pendingOperation.addEventListener(PendingOperation.RESULT,
searchForPhotosResultHandler);
}
You can see that the controller sets isSearching to true.
When the result is returned, not only does the handler method
update the dataProvider of the view,
but it also sets isSearching to
false:
private function searchForPhotosResultHandler(event:ResultEvent):void {
var photos:ArrayCollection = event.result as ArrayCollection;
ApplicationModel.getInstance().searchResults = photos;
view.dataProvider = photos;
view.isSearching = false;
}
Throughout this book, you learned a great many things about Flex 3. In this chapter, we tied much of that together with a sample application and looked at a set of simple best practices. In the course of this chapter, you had the opportunity to download a complete Flex 3 sample application that uses the Flickr service to search photos and display results. We then discussed some of the key architectural decisions in the application.
This excerpt is from Programming Flex 3. If you want to try your hand at developing rich Internet applications with Adobe's Flex 3, and already have experience with frameworks such as .NET or Java, this is the ideal book to get you started. Programming Flex 3 gives you a solid understanding of Flex 3's core concepts, and valuable insight into how, why, and when to use specific Flex features. Learn to get the most from this amazing and sophisticated technology.
Copyright © 2009 O'Reilly Media, Inc.