In the flex application we are currently developing, the need has arisen to get some more structure on the code. In the upcoming months, we are going to do a great deal of flex development, heavily expanding the functionality of the application, and of course we want to keep a maintainable application. Because of this, we are in the need of some structure and guidance on the architecture, hence the need for an architectural flex framework and some good patterns to follow.
In this post, I will discuss a specific area of the two current major architectural frameworks in flex, PureMVC and Cairngorm, namely how they apply patterns of GUI architecture.
What are Patterns of GUI Architecture?
As I previously wrote about in my post When Thin Clients Grow Fat, developing flex applications quickly reveal the need for the same kind of architectural guidance as when developing real, native, fat clients in frameworks like Swing and .Net Forms. To help architect such applications, some patterns on GUI architecture has been developed and described. Martin Fowler has some good descriptions of some of the more know, like Presentation Model and Supervising Presenter. These are not whole UI architectures like MVC, but smaller parts of architectural guidance, related to how the logic of the application relates to the api of the view framework.
How PureMVC Applies UI Patterns
PureMVC has a construct called a Mediator, which is just what it sounds like, an application of the Mediator pattern, to mediate bewteen the apis of the view and the apis of the rest of the application. This is a key construct, in how PureMVC implements the View-part of the MVC architecture, and is introduced to reduce the dependencies between the application and the view, hereby lowering the coupling in the entire system.
The PureMVC Framework Overview documentation has the following to say about mediators:
Mediators help us to create or reuse existing user interface components without the need to imbue them with knowledge about the PureMVC application they communicate with.
… View Components display data or accept user gestures. In a Flash-based application, they typically communicate with their Mediators using Events and exposing some properties for the concrete Mediator to inspect or manage. A Mediator connects a View Component with its data and communicates with the rest of the system on its behalf.
This, actually is not a very precise description of how to use a mediator in PureMVC. Luckily, PureMVC has an Implementation Idioms and Best Practices document, which lists an actual example, the LoginPanel example. In the example it shows, that only the mediator knows about the view, and that the view does not know about the mediator. Here is a source extract of the PureMVC documentation:
<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" title="Login" status="{loginStatus}">
<mx:MetaData>
[Event('tryLogin')];
</mx:MetaData>
<mx:Script><![CDATA[
import com.me.myapp.model.vo.LoginVO;
[Bindable] public var loginVO:LoginVO = new LoginVO();
[Bindable] public var loginStatus:String = NOT_LOGGED_IN;
public static const TRY_LOGIN:String = 'tryLogin';
public static const LOGGED_IN:String = 'Logged In';
public static const NOT_LOGGED_IN:String = 'Enter Credentials';
]]></mx:Script>
<mx:Binding source="username.text" destination="loginVO.username"/>
<mx:Binding source="password.text" destination="loginVO.password"/>
<mx:Form id="loginForm">
<mx:FormItem label="Username:">
<mx:TextInput id="username" text="{loginVO.username}"/>
</mx:FormItem>
<mx:FormItem label="Password:">
<mx:TextInput id="password" text="{loginVO.password}" displayAsPassword="true"/>
</mx:FormItem>
<mx:FormItem>
<mx:Button label="Login" enabled="{loginStatus == NOT_LOGGED_IN}" click="dispatchEvent( new Event(TRY_LOGIN, true ));"/>
</mx:FormItem>
</mx:Form>
</mx:Panel>
and the class mediating this view component:
package com.me.myapp.view {
import flash.events.Event;
import org.puremvc.as3.interfaces.*;
import org.puremvc.as3.patterns.mediator.Mediator;
import com.me.myapp.model.LoginProxy;
import com.me.myapp.model.vo.LoginVO;
import com.me.myapp.ApplicationFacade;
import com.me.myapp.view.components.LoginPanel;
public class LoginPanelMediator extends Mediator implements IMediator {
public static const NAME:String = 'LoginPanelMediator';
public function LoginPanelMediator(viewComponent:LoginPanel) {
super(NAME, viewComponent);
LoginPanel.addEventListener(LoginPanel.TRY_LOGIN, onTryLogin);
}
override public function listNotificationInterests() : Array {
return [ LoginProxy.LOGIN_FAILED, LoginProxy.LOGIN_SUCCESS ];
}
override public function handleNotification(note:INotification):void {
switch (note.getName()) {
case LoginProxy.LOGIN_FAILED:
LoginPanel.loginVO = new LoginVO();
loginPanel.loginStatus = LoginPanel.NOT_LOGGED_IN;
break;
case LoginProxy.LOGIN_SUCCESS:
loginPanel.loginStatus = LoginPanel.LOGGED_IN;
break;
}
}
private function onTryLogin(event:Event) : void {
sendNotification(ApplicationFacade.LOGIN, loginPanel.loginVO);
}
protected function get loginPanel() : LoginPanel {
return viewComponent as LoginPanel;
}
}
}
This pattern is what is known as either a Supervising Presenter or a Passive View. These patterns both extract behaviour out of the view and into a presentation class coupled with the view. In addition, in both patterns, this is done in a “presenter knows the view, but the view does not know the presenter”-way. What separates these two patterns from each other is how much logic to extract. In the case of a Passive View, all logic possible is extracted from the view, putting all responsibility on the presenter to control and update the view. This allows for easier testing but also a lot more work on the presenter class.
So, which flavour is the mediator? In my opinion, the mediator comes closest to being a Supervising Presenter, at least if I look at the LoginPanel example. In that example, model instances are used in the view, and databinding is used (still within the view), to bind form fields into the model instance. In addition to this, the documentation for the puremvc mediator says:
A View Component should encapsulate as much of its own state and operation as possible, exposing a simple API of events, methods and properties.
The responsibilities for the Mediator are primarily handling Events dispatched from the View Component and relevant Notifications sent from the rest of the system.
This shows, that a mediator is supposed to put some burden on the view, while keeping itself lean.
How Cairngorm Applies UI Patterns
Cairngorm has no notion of a mediator, a supervising presenter, passive view or anything like it. Actually, it has been bashed upon by many, as it advocates a solution of simply data-binding of view component state directly up into the model. And to make matters worse, the model is simply a global state, expressed through a singleton.
Looking at the Cairngorm documentation and the examples (both the introductory contact management app and the “advanced” Cairngorm Store), only make this stand out even more clear. The views have lots of logic, and as such operates as what is known as the Autonomous View pattern. So, what is an autonomous view? Martin Fowler describes it as:
Puts all presentation state and behavior for a window in a single class.
which to me is a bit like having a no pattern pattern. Simply chunk everything together. This, combined with the fact, that the autonomous views in a Cairngorm based application are advocated to databind directly into one global model instance, makes for really bad separation of concerns between the view and the model.
Even though I think that most of us can agree on the above being a really bad solution, it does not make all of Cairngorm bad. You “just” have to know this up front, and do some extra work yourself. One way to solve this in Cairngorm, is to introduce your own presentation class, separating the view better from the model. In addition to this, you get the option of choosing something else, than a supervising presenter as PureMVC advocates. But, you are ending up coding something to fix a mischild of Cairngorm, something that is already build into PureMVC. In addition to this, if you do not want to databind directly into the model, you will have to think of a way to get databound data from the presenter back into the real model.
Conclusion
So is PureMVC better then? This is a question, that cannot be answered by this post alone. The UI patterns is just one part of both frameworks, albeit an important one. One could argue, that you get more out-of-the-box with PureMVC, the mediators being a builtin concept of the framework. The mediators and the communication from and to mediators through notifications, have been nicely worked into the PureMVC framework.
Further Reading
If you found this article interesting and want to know more, I recommend looking at these resources: