PureMVC is not Flex but rather an ActionScript (AS) framework. PureMVC concentrates on the task of creating a generic framework for low-level AS objects; Flex comes with “prebuilt suggestions” for how a Model-View-Controller might work—and it offers lots of hooks throughout the data and UI classes that help implement MVC. But because Flex, AIR, and Flash understand this language, PureMVC can be used in any applications built in any of these environments.
Similarly to Cairngorm, PureMVC is built on singletons. The Model
, View
,
Controller
, and Facade
classes are singletons. In Cairngorm,
developers need to write code to instantiate each singleton; in PureMVC,
only the Facade
class has to be
instantiated in the application code and creation of the Model
, View
,
and Controller
classes is done by the
Facade
class itself.
In Cairngorm, you create an application-specific FrontController
and register event-command
pairs; in PureMVC, you create a Facade
class and
register notification-command pairs
there. With PureMVC, you can execute multiple commands as a reaction to a notification.
Object-oriented programming languages arrange event-driven communication between the objects by implementing the Observer design pattern. An observer object is registered with one or more observable objects that generate notifications to be consumed by the observer.
Cliff Hall, the author of PureMVC, went the same route to ensure
that this framework can be used even in non-Flash environments that don’t
offer flash.events.Event
and EventDispatcher
classes.
Views are controlled by their mediator objects, which maintain maps of notifications and their observers.
Notifications are a PureMVC implementation of event-driven communication between application components. The author of PureMVC wanted to make this framework portable to other languages; hence standard Flash events are not used in the framework, even though Flex developers still can use regular events to process, say, button clicks.
Although flash.events.Event
is
not leveraged by the PureMVC framework, the Notification
class has the property
called body
typed as Object
, which is a place for storing
application-specific data that may need to be carried by a notification
object. In pure ActionScript, you’d have to create a custom event object
providing a placeholder for the custom data (on the other hand, in custom
ActionScript events, the data can be strongly typed as opposed to being
just Object
s).
To better understand this framework, take a walk through the code of Café Townsend that was ported to PureMVC by Michael Ramirez. Please download this application at http://trac.puremvc.org/Demo_AS3_Flex_CafeTownsend.
The data flow between PureMVC components while displaying a list of Café employees is depicted in Figure 1-8.
Your goal remains the same: walk the route that would display the list of Café employees. Figure 1-9 shows the structure of this application in Flash Builder.
The code of the CafeTownsend.mxml application is shown in
Example 1-15. You’ll see a
familiar ViewStack
container that
holds employee login, list, and detail views. It declares the variable
facade
, which holds the reference to the ApplicationFacade
singleton that is created
during initializing the value of this variable. Then the method startup()
is called on this ApplicationFacade
object inherited from
PureMVC’s Facade
class.
Example 1-15. CafeTownsend.mxml—the application
<?xml version="1.0"?> <!-- PureMVC AS3 Demo - Flex CafeTownsend Copyright (c) 2007-08 Michael Ramirez <michael.ramirez@puremvc.org> Parts Copyright (c) 2005-07 Adobe Systems, Inc. Your reuse is governed by the Creative Commons Attribution 3.0 License --> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:view="org.puremvc.as3.demos.flex.cafetownsend.view.components.*" xmlns:mvc="org.puremvc.as3.demos.flex.cafetownsend.*" layout="vertical" backgroundColor="#000000" creationComplete="facade.startup(this)"> <mx:Script> <![CDATA[ import org.puremvc.as3.demos.flex.cafetownsend.*; private var facade:ApplicationFacade = ApplicationFacade.getInstance(); ]]> </mx:Script> <mx:Style source="assets/main.css" /> <mx:Image source="@Embed('assets/header.jpg')" width="700" /> <mx:HBox paddingBottom="10" paddingLeft="10" paddingRight="10" paddingTop="10" backgroundColor="#ffffff" width="700"> <mx:VBox width="100%" verticalScrollPolicy="off" paddingRight="10"> <mx:ViewStack id="vwStack" width="100%" paddingBottom="10" paddingTop="10" resizeToContent="true" creationPolicy="all"> <view:EmployeeLogin id="employeeLogin" /> <view:EmployeeList id="employeeList" /> <view:EmployeeDetail id="employeeDetail" /> </mx:ViewStack> </mx:VBox> </mx:HBox> </mx:Application>
During creation of the Facade
instance (see
Example 1-16), PureMVC automatically
initializes the instances of Model
,
View
, and Controller
classes, and if you need to execute
application-specific code during this process, override the appropriate
initialize method.
Example 1-16. ApplicationFacade.as
/* PureMVC AS3 Demo - Flex CafeTownsend Copyright (c) 2007-08 Michael Ramirez <michael.ramirez@puremvc.org> Parts Copyright (c) 2005-07 Adobe Systems, Inc. Your reuse is governed by the Creative Commons Attribution 3.0 License */ package org.puremvc.as3.demos.flex.cafetownsend{ import org.puremvc.as3.interfaces.*; import org.puremvc.as3.patterns.proxy.*; import org.puremvc.as3.patterns.facade.*; import org.puremvc.as3.demos.flex.cafetownsend.view.*; import org.puremvc.as3.demos.flex.cafetownsend.model.*; import org.puremvc.as3.demos.flex.cafetownsend.controller.*; /** * A concrete <code>Facade</code> for the <code>CafeTownsend</code> application. * The main job of the <code>ApplicationFacade</code> is to act as a single * place for mediators, proxies, and commands to access and communicate * with each other without having to interact with the Model, View, and * Controller classes directly. All this capability it inherits from * the PureMVC Facade class.</P> * This concrete Facade subclass is also a central place to define * notification constants which will be shared among commands, proxies, and * mediators, as well as initializing the controller with Command to * Notification mappings.</P> */ public class ApplicationFacade extends Facade { // Notification name constants public static const STARTUP:String= "startup"; public static const SHUTDOWN:String= "shutdown"; public static const APP_LOGOUT:String= "appLogout"; public static const APP_LOGIN:String= "appLogin"; public static const LOAD_EMPLOYEES_SUCCESS:String="loadEmployeesSuccess"; public static const LOAD_EMPLOYEES_FAILED:String="loadEmployeesFailed"; public static const VIEW_EMPLOYEE_LOGIN:String= "viewEmployeeLogin"; public static const VIEW_EMPLOYEE_LIST:String= "viewEmployeeList"; public static const VIEW_EMPLOYEE_DETAIL:String= "viewEmployeeDetail"; public static const ADD_EMPLOYEE:String= "addEmployee"; public static const UPDATE_EMPLOYEE:String= "updateEmployee"; public static const SAVE_EMPLOYEE:String= "saveEmployee"; public static const DELETE_EMPLOYEE:String = "deleteEmployee"; /** * Singleton ApplicationFacade Factory Method */ public static function getInstance() : ApplicationFacade{ if ( instance == null ) instance = new ApplicationFacade( ); return instance as ApplicationFacade; } /** * Register Commands with the Controller */ override protected function initializeController( ) : void { super.initializeController(); registerCommand( STARTUP, ApplicationStartupCommand ); } public function startup( app:CafeTownsend ):void{ sendNotification( STARTUP, app ); } } }
In Example 1-16, during controller
initialization, the STARTUP notification is registered with the command
class ApplicationStartupCommand
. So
far it looks pretty similar to Cairngorm’s FrontController
from Example 1-4, doesn’t it?
But PureMVC allows you to invoke more than one command as a
response to a notification. For example, the author of this version of
Café Townsend decided to invoke two commands during the application
startup—ModelPrepCommand
and ViewPrepCommand
. When your command class
extends MacroCommand
, you are allowed
to register a sequence of
subcommands, and the ApplicationStartupCommand
looks like Example 1-17.
Example 1-17. ApplicationStartupCommand.as
/* PureMVC AS3 Demo - Flex CafeTownsend Copyright (c) 2007-08 Michael Ramirez <michael.ramirez@puremvc.org> Parts Copyright (c) 2005-07 Adobe Systems, Inc. Your reuse is governed by the Creative Commons Attribution 3.0 License*/ package org.puremvc.as3.demos.flex.cafetownsend.controller { import org.puremvc.as3.patterns.com7mand.*; import org.puremvc.as3.interfaces.*; /** * A MacroCommand executed when the application starts. */ public class ApplicationStartupCommand extends MacroCommand { override protected function initializeMacroCommand() :void{ addSubCommand( ModelPrepCommand ); addSubCommand( ViewPrepCommand ); } } }
We’ll follow the model preparation route at this point, but we’ll
get back to ViewPrepCommand
in Example 1-22.
After the controller tier that routes commands come the proxy
classes that deal with both—data models and the service calls if need
be. Let’s follow the ModelPrepCommand
(Example 1-18). It registers employee and user
proxy classes with the Facade
class, so they know
where to send notifications.
Example 1-18. ModelPrepCommand.as
/*PureMVC AS3 Demo - Flex CafeTownsend Copyright (c) 2007-08 Michael Ramirez <michael.ramirez@puremvc.org> Parts Copyright (c) 2005-07 Adobe Systems, Inc. Your reuse is governed by the Creative Commons Attribution 3.0 License */ package org.puremvc.as3.demos.flex.cafetownsend.controller { import org.puremvc.as3.interfaces.*; import org.puremvc.as3.patterns.command.*; import org.puremvc.as3.patterns.observer.*; import org.puremvc.as3.demos.flex.cafetownsend.*; import org.puremvc.as3.demos.flex.cafetownsend.model.*; /** * Create and register <code>Proxy</code>s with the <code>Model</code>. */ public class ModelPrepCommand extends SimpleCommand{ override public function execute( note:INotification ) :void{ facade.registerProxy(new EmployeeProxy()); facade.registerProxy(new UserProxy()); } } }
We are about halfway through the process of getting the employee
list with PureMVC. This time, we’ll just get familiar with a fragment of
the code for the EmployeeProxy
class
(Example 1-19).
Example 1-19. A fragment of EmployeeProxy.as
public class EmployeeProxy extends Proxy implements IResponder { public static const NAME:String = "EmployeeProxy"; public var errorStatus:String; public function EmployeeProxy ( data:Object = null ){ super ( NAME, data ); } public function loadEmployees():void{// create a worker who will go get some data; pass it a reference to
// this proxy so the delegate knows where to return the data
var delegate : LoadEmployeesDelegate =new LoadEmployeesDelegate(this );// make the delegate do some work
delegate.loadEmployeesService(); }// this is called when the delegate receives a result from the service
public function result( rpcEvent : Object ) : void{// populate the employee list in the proxy with the results
// from the service call
data = rpcEvent.result.employees.employee as ArrayCollection; sendNotification( ApplicationFacade.LOAD_EMPLOYEES_SUCCESS ); }// this is called when the delegate receives a fault from the service
public function fault( rpcEvent : Object ) : void { data = new ArrayCollection();// store an error message in the proxy
// labels, alerts, etc can bind to this to notify the user of errors
errorStatus = "Could Not Load Employee List!"; sendNotification( ApplicationFacade.LOAD_EMPLOYEES_FAILED ); }
Proxies link the data model with services. The model is
represented by the variable data that’s predefined in the superclass.
The service is available via the delegate class, which in this version
of Café Townsend is called LoadEmployeesDelegate
. Because EmployeeProxy
implements the IResponder
interface, it must include the
methods result()
and fault()
. In the case of success, the variable
data is populated with the retrieved list of employees and notification
LOAD_EMPLOYEES_SUCCESS
is sent to
whoever is interested in hearing about it—you can take a peek at the
method listNotificationInterests()
in
Example 1-21. In the case of failure, this
version of Café Townsend just assigns a value to the variable errorStatus
and sends the notification
LOAD_EMPLOYEES_FAILED
.
As you can see in Example 1-20, the
delegate class to load employees has nothing specific to PureMVC—it just
sets the responder and uses HTTPService
to read the file Employees.xml.
Example 1-20. LoadEmployeesDelegate.as
/* PureMVC AS3 Demo - Flex CafeTownsend Copyright (c) 2007-08 Michael Ramirez <michael.ramirez@puremvc.org> Parts Copyright (c) 2005-07 Adobe Systems, Inc. Your reuse is governed by the Creative Commons Attribution 3.0 License */ package org.puremvc.as3.demos.flex.cafetownsend.model.business { import mx.rpc.AsyncToken; import mx.rpc.IResponder; import mx.rpc.http.HTTPService; public class LoadEmployeesDelegate{ private var responder : IResponder; private var service : HTTPService; public function LoadEmployeesDelegate( responder : IResponder ) { this.service = new HTTPService(); this.service.url="assets/Employees.xml"; // store a reference to the proxy that created this delegate this.responder = responder; } public function loadEmployeesService() : void { // call the service var token:AsyncToken = service.send(); // notify this responder when the service call completes token.addResponder( responder ); } } }
Now trace how the employees will arrive to the View. The view tier in PureMVC has two players: the UI component and the mediator class. Chapter 2 discusses the Mediator pattern, but in general, its role is to arrange the communication of two or more components without them knowing about each other. For example, an application container has a shopping cart component and a product list component. When the user makes a selection, the product component sends an event carrying the selected product to the mediator (e.g., an application), which forwards it to the shopping cart component.
But PureMVC mediators play the role of middlemen between the UI
components and proxy objects (not controllers), and the need for these
middlemen is questionable. In our opinion, it would be cleaner to
introduce a value object and pass it directly (in the body of Notification
) between the view and its
controller rather than having the mediator reaching out to internals of
both the proxy and the view. But it is what it is, and the EmployeeList
view interacts with the EmployeeListMediator
, and the latter deals
with the controller’s notifications.
In Example 1-21, note the method
listNotificationInterests()
, where
you, the developer, have to list all events this mediator is interested
in (similar to a subscription in messaging). The method handleNotification()
will process
notifications when they arrive.
Example 1-21. EmployeeListMediator.as
/* PureMVC AS3 Demo - Flex CafeTownsend Copyright (c) 2007-08 Michael Ramirez <michael.ramirez@puremvc.org> Parts Copyright (c) 2005-07 Adobe Systems, Inc. Your reuse is governed by the Creative Commons Attribution 3.0 License */ package org.puremvc.as3.demos.flex.cafetownsend.view{ import flash.events.Event; import org.puremvc.as3.interfaces.*; import org.puremvc.as3.patterns.mediator.Mediator; import org.puremvc.as3.demos.flex.cafetownsend.ApplicationFacade; import org.puremvc.as3.demos.flex.cafetownsend.view.components.*; import org.puremvc.as3.demos.flex.cafetownsend.model.EmployeeProxy; /** * A Mediator for interacting with the EmployeeList component */ public class EmployeeListMediator extends Mediator{ public static const NAME:String = "EmployeeListMediator"; public function EmployeeListMediator( viewComponent:Object ){ // pass the viewComponent to the superclass where // it will be stored in the inherited viewComponent property super( NAME, viewComponent ); employeeProxy = EmployeeProxy( facade.retrieveProxy( EmployeeProxy.NAME ) ); employeeList.addEventListener( EmployeeList.APP_LOGOUT, logout ); employeeList.addEventListener( EmployeeList.ADD_EMPLOYEE, addEmployee ); employeeList.addEventListener( EmployeeList.UPDATE_EMPLOYEE, updateEmployee ); } /** * List all notifications this Mediator is interested in. * Automatically called by the framework when the mediator * is registered with the view. * @return Array the list of Notification names */ override public function listNotificationInterests():Array{ return [ ApplicationFacade.LOAD_EMPLOYEES_SUCCESS, ApplicationFacade.LOAD_EMPLOYEES_FAILED ]; } /** * Handle all notifications this Mediator is interested in. * <P> * Called by the framework when a notification is sent that * this mediator expressed an interest in when registered * (see <code>listNotificationInterests</code>.</P> * * @param INotification a notification */ override public function handleNotification(note:INotification ):void{ switch ( note.getName() ) { case ApplicationFacade.LOAD_EMPLOYEES_SUCCESS: employeeList.employees_li.dataProvider = employeeProxy.employeeListDP; break; case ApplicationFacade.LOAD_EMPLOYEES_FAILED: employeeList.error.text = employeeProxy.errorStatus; break; } } /** * Cast the viewComponent to its actual type. * * This is a useful idiom for mediators. The * PureMVC Mediator class defines a viewComponent * property of type Object. </P> * * @return EmployeeList the viewComponent cast to EmployeeList */ protected function get employeeList():EmployeeList{ return viewComponent as EmployeeList; } private function logout( event:Event = null ):void{ sendNotification( ApplicationFacade.APP_LOGOUT ); } private function addEmployee( event:Event = null ):void{ sendNotification( ApplicationFacade.ADD_EMPLOYEE ); } private function updateEmployee( event:Event = null ):void{ sendNotification( ApplicationFacade.UPDATE_EMPLOYEE, employeeList.employees_li.selectedItem); } private var employeeProxy:EmployeeProxy; } }
The code of handleNotification()
directly manipulates the
internals of the view components (e.g., employeeList.employees_li
), which leads to
tight coupling between the mediator and the view. If the next version of
the employeeList
component will use a
DataGrid
instead of the List
component, the mediator’s code has to be
refactored, too.
The previous discussion of Example 1-17 did not cover the process of preparing the view for receiving the events. Handling that process is the branch of code originated by the following call:
addSubCommand( ViewPrepCommand );
Shown in Example 1-22, the ViewPrepCommand
class registers the main
application mediator (you’d have to write it), and asks the proxy to
load the employee list.
Example 1-22. ViewPrepCommand.as
/* PureMVC AS3 Demo - Flex CafeTownsend Copyright (c) 2007-08 Michael Ramirez <michael.ramirez@puremvc.org> Parts Copyright (c) 2005-07 Adobe Systems, Inc. Your reuse is governed by the Creative Commons Attribution 3.0 License */ package org.puremvc.as3.demos.flex.cafetownsend.controller{ import org.puremvc.as3.interfaces.*; import org.puremvc.as3.patterns.command.*; import org.puremvc.as3.patterns.observer.*; import org.puremvc.as3.demos.flex.cafetownsend.*; import org.puremvc.as3.demos.flex.cafetownsend.model.*; import org.puremvc.as3.demos.flex.cafetownsend.view.ApplicationMediator; /** * Prepare the View for use. * The Notification was sent by the Application, and a reference to that * view component was passed on the note body. * The ApplicationMediator will be created and registered using this * reference. The ApplicationMediator will then register * all the Mediators for the components it created. */ public class ViewPrepCommand extends SimpleCommand{ override public function execute( note:INotification ) :void{ // Register your ApplicationMediator facade.registerMediator( new ApplicationMediator( note.getBody())); // Get the EmployeeProxy var employeeProxy:EmployeeProxy = facade.retrieveProxy( EmployeeProxy.NAME ) as EmployeeProxy; employeeProxy.loadEmployees(); sendNotification( ApplicationFacade.VIEW_EMPLOYEE_LOGIN ); } } }
This command class issues a request to load employees without even
waiting for the successful logon of the user. At the end of the execute()
method, this code sends the VIEW_EMPLOYEE_LOGIN
notification, which
displays the logon view.
For brevity, Example 1-23 does have
most of the comments from the code of ApplicationMediator
. It builds all
view components and registers the mediators for each of them.
Example 1-23. ApplicationMediator.as
/* PureMVC AS3 Demo - Flex CafeTownsend Copyright (c) 2007-08 Michael Ramirez <michael.ramirez@puremvc.org> Parts Copyright (c) 2005-07 Adobe Systems, Inc. Your reuse is governed by the Creative Commons Attribution 3.0 License*/ package org.puremvc.as3.demos.flex.cafetownsend.view { public class ApplicationMediator extends Mediator{ public static const NAME:String = "ApplicationMediator"; public static const EMPLOYEE_LOGIN : Number = 0; public static const EMPLOYEE_LIST : Number = 1; public static const EMPLOYEE_DETAIL : Number = 2; public function ApplicationMediator( viewComponent:Object ) { // pass the viewComponent to the superclass where // it will be stored in the inherited viewComponent property super( NAME, viewComponent ); // Create and register Mediators for the Employee // components that were instantiated by the mxml application facade.registerMediator( new EmployeeDetailMediator( app.employeeDetail ) ); facade.registerMediator( new EmployeeListMediator( app.employeeList ) ); facade.registerMediator( new EmployeeLoginMediator( app.employeeLogin ) ); // retrieve and cache a reference to frequently accessed proxys employeeProxy = EmployeeProxy( facade.retrieveProxy( EmployeeProxy.NAME ) ); userProxy = UserProxy( facade.retrieveProxy( UserProxy.NAME ) ); } override public function listNotificationInterests():Array { return [ ApplicationFacade.VIEW_EMPLOYEE_LOGIN, ApplicationFacade.VIEW_EMPLOYEE_LIST, ApplicationFacade.VIEW_EMPLOYEE_DETAIL, ApplicationFacade.APP_LOGOUT, ApplicationFacade.UPDATE_EMPLOYEE ]; } /** * Handle all notifications this Mediator is interested in. */ override public function handleNotification( note:INotification ):void{ switch ( note.getName() ){ case ApplicationFacade.VIEW_EMPLOYEE_LOGIN: app.vwStack.selectedIndex = EMPLOYEE_LOGIN; break; case ApplicationFacade.VIEW_EMPLOYEE_LIST: employeeProxy.employee = null; app.vwStack.selectedIndex = EMPLOYEE_LIST; break; case ApplicationFacade.VIEW_EMPLOYEE_DETAIL: app.vwStack.selectedIndex = EMPLOYEE_DETAIL; break; case ApplicationFacade.APP_LOGOUT: app.vwStack.selectedIndex = EMPLOYEE_LOGIN; break; case ApplicationFacade.UPDATE_EMPLOYEE: app.vwStack.selectedIndex = EMPLOYEE_DETAIL; break; } } /** * Cast the viewComponent to its actual type. * The PureMVC Mediator class defines a viewComponent * property of type Object. */ protected function get app():CafeTownsend{ return viewComponent as CafeTownsend } // Cached references to needed proxies private var employeeProxy:EmployeeProxy; private var userProxy:UserProxy; } }
The ApplicationMediator
is also
a central repository of all proxies that know how to get the data
(EmployeeProxy
and UserProxy
in our case). So the ViewPrepCommand
creates an instance of the
ApplicationMediator
(which creates
other mediators and proxies to be cached), registers it with the facade,
and asks the facade for a newly created instance of the EmployeeProxy
, and calls its loadEmployees()
method.
If the EmployeeProxy
successfully retrieves the employee, it triggers the notification
LOAD_EMPLOYEES_SUCCESS
, which the
EmployeeMediator
processes, putting
the data in the data provider of the EmployeeList
(see Example 1-21 earlier):
case ApplicationFacade.LOAD_EMPLOYEES_SUCCESS: employeeList.employees_li.dataProvider = employeeProxy.employeeListDP;
The circle is closed. As you can see, the PureMVC way to bring Café Townsend’s employee list is a lot more complicated than the Cairngorm or Mate way.
Still, if you work with an application built on the PureMVC framework, consider using a freeware product by Kap IT called PureMVC Console, available at http://lab.kapit.fr/display/puremvcconsole/PureMVC+Console. This tool comes in handy if you’ve joined a PureMVC project and need to hit the ground running. This console allows you to monitor the internal flow of this framework in real time. The creators of PureMVC Console offer a nice demo of monitoring Café Townsend—check it out at the website.
The MultiCore version of PureMVC supports modular programming where singletons are replaced with so-called Multiton Core actors.
We are having difficulty finding reasons for recommending an architectural framework that requires developers to replace 20 lines of code from Example 1-1 with all the code shown in Examples 1-15 through 1-23 to achieve the same goal: display the list of employees from an XML file in a list control.
The author of PureMVC wanted to create a framework that could have been ported to other programming languages, and this approach inadvertently delivers a product that underutilizes benefits offered by language-specific constructs. Because PureMVC was not created specifically for Flex, it doesn’t take advantage of the declarative nature of MXML, which would’ve substantially minimized the amount of handwritten code by application developers. For the same reason, PureMVC doesn’t use standard Flex events and data binding. As an old saying goes, “When in Rome, speak Latin.” It can be rephrased as, “When in Flex, speak MXML and ActionScript.”
The pros are:
It’s well documented.
It supports working with Flex modules.
It’s available for developers who want to use only ActionScript (e.g., Flash programmers). For Flex programmers, though, that can’t be considered a benefit.
The cons are:
It’s not a framework written for Flex, and thus does not use features offered by MXML.
It has too many layers, which are tightly coupled.
It requires staffing projects with more senior developers.
Developers have to write lots of additional classes, which adds to the project timeline.
Its standard version is built on singletons, and application code becomes cluttered by making multiple calls to them.
One of the main Flex selling points is its MXML-to-ActionScript code generator, which spares application developers from manually writing lots of code. PureMVC doesn’t use MXML and forces developers to write more code, which makes them less productive.
PureMVC notifications are more flexible than event maps of Mate,
in that the latter relies on the enabled event bubbling, and if the
EventMap
object is not located in the ancestor of the
object that triggers the event, it won’t get it. As a workaround, Mate
offers a special Dispatcher
class to
trigger events, say from a pop-up window that is not a descendant of an
Application
object. But in PureMVC,
any object can subscribe for any other object’s notifications regardless
of their relations. Also, since the Notification
class already has the property
body
to carry additional payload,
application developers don’t need to create subclasses for each
notification object.
PureMVC has too many layers, dependencies, and singletons, and as a result has a steeper learning curve than Cairngorm or Mate. Managers on the projects that use PureMVC would need to hire more experienced developers than managers on projects using Mate or Cairngorm.
Note
PureMVC Console is a convenient tool allowing you to monitor the Cairngorm and PureMVC applications; see http://lab.kapit.fr. To monitor the PureMVC version of Café Townsend, click on the image of the Café at http://lab.kapit.fr/display/puremvcconsole/PureMVC+Console.
PureMVC documentation states, “The PureMVC framework has a very narrow main goal: to help you separate your application’s coding concerns into three discrete tiers; Model, View, and Controller.” The framework attempts to achieve this goal by forcing application developers to write a lot of additional ActionScript code.
Unit testing of separate parts of the PureMVC application is nontrivial, because each test case would require additional work to register notifications, mediators, and other objects.
Get Agile Enterprise Application Development with Flex now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.