Understanding the Core Classes
The foundational package for building Flex UIComponents is Adobe’s widespread enterprise level framework, Cairngorm. Universal Mind has added several extensions to Cairngorm, including advanced FrontController and Command classes as well as enhanced Events and View Notification patterns. Inspired by Steve Reiner from IntegratedSemantics.org and Paul Williams from Adobe, I will also integrate into this Flex UIComponent Framework their work on the Passive View Presentation Pattern, originally conceived by Martin Fowler. Together, these Design Patterns come together into what is called the Model-View-Presenter pattern (MVP) for the Presentation and the Model-View-Controller pattern for the entire component.
In this post I will go over how the classes in these design patterns communicate with each other and how to think about it when building a Flex UIComponent. Check out my last post to get a general overview of how this pattern works.
The Model:
The biggest problem you run into when trying to build a Flex UIComponent is in determining the distinction was between the Model and View. Some places said the Model was a collection of properties while others said it contained business logic. The CairngormStore example (source) demonstrates this too: the shopping cart model has some logic on how to add items to the cart, but it leaves out other things. Compare this Model to the CairngormLogin example whose model contains just a list of properties and a property holding the state; it doesn’t have business logic like the Shopping Cart does in the CairngormStore. So how do you separate out the Model from the View? And how do you determine what the Model should have?
It turns out that you don’t always need a Model. Well that makes life easy! If only they said that from the start… You only need the model if you have global properties or states you are trying to keep track of. In addition, Views often have references to many Models from different components, not just their own! This makes things complicated for the beginner, so keep those two points in mind.
When we build our Flex UIComponents, the Model should consist only of the following:
- Properties: Booleans (such as State), Strings, Numbers, Value Objects
- Functions that result in Properites: XML Parser (parse XML into properties), adding elements to an Array property, getters and setters, etc.
This makes our life much easier. Let’s use our future DiagramUI component as an example. To keep it simple, say we have an “Edit” button that allows a panel of buttons to become modifiable. What is the Model for this “Edit” button? The only thing that will change globally when the Edit button is pressed in the DiagramUI is the state of the DiagramPanel: it can be either EDITABLE or FIXED. This is a boolean value and thus qualifies as a Model property. Thus, the DiagramPanel Model can be composed of this single boolean state property, though it isn’t limited to this.
Another example… If a DiagramNode on the DiagramPanel were to load something, say a text file, then the DiagramNode Model could start by having a “loadedFile” boolean value. When an Event occurred that asked the DiagramNode to load a file, the DiagramNode Model would change it’s state from “loadedFile = false” to “loadedFile = true”, and the DiagramNode component (View and Presenter) would no longer be able to load a file. One last example… If the DiagramPanel were to load its Diagram Nodes based on an XML file that specified Node titles, locations, connections, etc., the DiagramPanel Model would have a function that resulted in those Node properties, an “initProperties()” function that parsed the XML data into Node properties. These are the types of things that are kept in the Model. Everything else is probably in the ViewBase or the ViewPresenter.
The View:
The View (or Presentation) is a little bit more tricky. It is almost a mini MVC pattern in itself, presumably the reason why it’s called the Model-View-Presenter (MVP). Take a look at Paul Williams analysis of 6 different Presentation patterns to see how tricky this is. Cairngorm has been criticized for having an “Autonomous View”, meaning that the View keeps track of not only its state but also how to change its state, listen for Events, and perform all kinds of functions. This makes the View just one big mess of functions, event listeners, and properties.
The two Presentation patterns I find interesting are the Supervising Presenter and the Passive View. Download the two and compare the differences between their MVP implementations for reference. Both keep the state in the View and the logic in the Presenter, but in the Supervising Presenter the View.mxml file uses data binding to be updated, and so some of the state-changing logic is held in the View class. Williams says this makes Unit Testing more difficult as it more tightly couples the View to its Presentation logic. The Passive View, on the other hand, only allows the View to specify static properties, and so the state-changing logic is held in the Presenter class instead of the View.
For our purposes we will call the traditional View an MVP and focus on the Passive View implementation of the MVP. There is also a Presentation Model pattern (compare this to the Supervising Presenter and Passive View patterns), but you have to include some Actionscript code in the MXML file and that just makes things ugly and complicated. Nevertheless, the Passive View MVP is divided into three parts :
- View
- ViewBase
- Presenter
The View is an MXML file that has two main functions: add a visual object, the ViewBase, to the computer screen, and let the Presenter update the ViewBase any time there is some Event. That means it can have one or two lines of MXML code, or, if you want to specify some static properties (size, id, title, etc.) you can add those too. The View has the UIComponent’s state but not its logic.
The ViewBase is very simple too. I include the ViewBase because Steve Reiner’s FlexSpaces architecture is excellent (Paul Williams hasn’t included it his Album examples). Check out how the View, ViewBase, and Presenter work in his FlexSpaces project and compare it to Paul Higgins work to see how all this works. The ViewBase is an Actionscript file that either simply extends another Flex UIComponent or adds to the UIComponent by creating public variables that reference other components needed to build your ViewBase. A FolderViewBase that lists folders will simply extend a UIComponent (Canvas, Panel, etc.) and have public variables for a VBox, Grid, FolderDisplayBase, whatever. Essentially it is just a list of variables. The ViewBase is added to the display in the View.mxml class and modified in the Presenter.
The Presenter is the most complex of the MVP trio. The Presenter is an Actionscript class that takes into its constructor the ViewBase class and performs functions on it according to user input and other events. In the case of our DiagramUI Flex UIComponent, the Presenter will listen for events (such as a ChangeToEditable Event) and will 1) change the Model of the DiagramPanel by changing it's boolean state property and 2) animate or modify the View's state accordingly (such as making certain buttons functional and other not, or changing the color of the back panel). The Presenter, thus, knows about the Model and the View (really the ViewBase since the View just displays the ViewBase), has all state-changing logic, and the View doesn't have to worry about a thing; the View doesn't even know the Presenter exists.
The Control:
The Control package consists of five main parts:
- Events
- Controllers
- Commands
- Delegates
- Services
Cairngorm does this differently by dividing its packages into Business (Delegates and Services), Commands, Control, and Events, but Steve Reiner’s style as he did it in FlexSpaces is more intuitive. After some UserInputEvent on the View, an Event is dispatched from the Presenter class. This Event is handled by a Controller, elsewhere called the Front Controller, Boundary Controller, or App Controller. When the Controller receives the Event, it instantiates the appropriate Command and calls “execute()”.
Event
public class CustomEvent extends CairngormEvent
{
//type:String
public static const EVENT_TYPE:String = "event_type";
public static const ANOTHER_TYPE:String = "another_event_type";
//If you want to attach properties or information to an event,
//you can do that easily!
public var customValueObject:ValueObject;
public function CustomEvent(type:String, vo:ValueObject)
{
super(type);
this.customValueObject = vo;
}
override public function clone():Event
{
return new CustomEvent(type, customValueObject);
}
}
Controller
public class MySpecificController extends FrontController
{
public function MySpecificController()
{
//The addCommand() function is handled in the FrontController super class.
addCommand(CustomEvent.EVENT_TYPE, CustomEventCommand);
addCommand(CustomEvent.ANOTHER_EVENT, CustomEventCommand);
addCommand(AnotherEvent.EVENT_X, AnotherCommand);
}
}
Command
public class CustomCommand extends Command
{
public function CustomCommand()
{
super();
}
override public function execute(event:CairngormEvent):void
{
//this is where you change the model...
model.loadedFile = false
//or model.oldState = newState
//If you were wondering, if you had a delegate, this is the
//general form using Universal Mind's Cairngorm Extension
//var handlers:Callbacks = new Callbacks(onSuccess, onFault);
//var delegate:CustomDelegate = new CustomDelegate(handlers);
//delegate.doSomeAction(event.valueObject);
}
}
The Command can do either of two things from here, depending on your requirements. If you do not need to access remote services (such as a web sites RSS feed through HTTP requests, or a Java class through an open API like Alfresco’s Java API), then your Command can simply change the Model. In the case of our DiagramUI, a ChangeToEditable Event would invoke the ChangeToEditable Command that would then change the DiagramPanel “edit” state from true to false. The Presenter, which is observing the Model, will immediately be notified of this change and will then update the View (through the ViewBase) accordingly. If, on the other hand, your Command needs to access a remote service, the process is a bit more complex. This requires a Delegate.
The Delegate:
Universal Mind’s Cairngorm extension handles remote calls much more powerfully than Cairngorm’s delegate itself, but we won’t get into this now because we won’t be accessing remote services or doing any HTTP method calls. We will come back to this when we check out FlexSpaces for Alfresco. Delegates deserve their own post.
Summary:
What I have just outlined is a reusable framework for building Flex UIComponents that consists of the following:
- Cairngorm’s MVC Framework
- Passive View MVP Pattern
- Universal Mind Cairngorm Extensions
With this you can start thinking about how you would model anything you want, from a picture gallery built in Papervision 3D that gets images from Flickr to a Google Search Flex Display that accesses Google’s RESTful through HTTP requests and displays them in a cool mind web thingy. ANYTHING, you can model it according to this framework!
Coming up Next…
In the next post we will begin modeling our Diagram User Interface that will allow you to build a hierarchical diagram with crazy functionality. We will work through how to model it using this Flex UIComponent framework and will see just how amazing all this stuff really is. Until then…
Note: The CairngormStore and CairngormLogin examples don’t implement either the Universal Mind Cairngorm Extension or the Passive View MVP pattern. They are there to show how the Control sequence works.
If you enjoyed this post, make sure you subscribe to my RSS feed!