Simple MVC with ActionScript 3 - An Updated Approach
Back in October of 2008, I wrote an article on implementing a simple MVC micro-architecture. Apparently it was well received, and is by far the page on my site that gets the most traffic (about 5 times the amount).
Well that was roughly 18 months ago, and since then I have started using a much more efficient, flexible, and low footprint implementation. The goals of the new framework were to build one that allowed components to be reused, created better separation of concerns, made the application structure more modular, and gave the developer the freedom to structure the app the way it worked best for that project.
What was wrong with the old implementation?
First of all, the old implementation allowed for really only one Controller. This controller was used as the document class, and then passed as reference to the other elements of the framework. For larger projects, this approach would force the single controller to become bloated with actions, many of which would be totally unrelated to one another.
Second, the passing of reference to the Views and the subsequent direct calls from the Views to the Controller broke the loose coupling best practices. If you wanted to reuse one of the view components, you would likely have to strip out many of these direct calls in order to use a different controller in a different project.
Third, the views were setup using the Decorator Pattern. Now, there is nothing wrong with the Decorator Pattern. It makes a lot of sense in a lot of scenarios. The problem in this case is that the class controlling the functionality of the View was no longer a part of the Display List. So, events dispatched always had to be dispatched from the decorated object, which I just didn't like too much. As well, if you needed to effect the decorated DisplayObject before it had been added to stage, this often was not possible since the objects on the stage we instantiated when the DocClass (aka Controller) was instantiated, and so by the time the View decorator classes were instantiated the next frame had been rendered and you would end up with som unpredictable results with objects flashing on the screen, or appearing incorrectly for a frame.
So whats the new approach?
The new approach, like I mentioned, tries to make the app more readable, more modular, more reusable, and more decoupled. This was accomplished by 1) Creating a lower footprint for the framework, 2) Embracing the Display List, and 3) Borrowing a bit from Flex.
A few things to point out about this approach is, just like the last implementation, this is built to work using the Flash IDE and pure AS3. There is no dependencies on the Flex framework. In fact, I would recommend if you need MVC for a Flex project, use something else like: Mate, Swiz, Parsley, or Robotlegs.
The Document Class
Where the document class used to act as the controller for the entire application, it now acts as more of an initializer. It is used to load any config files, handle the preloading of the app, and then advance to the next frame when preloading is complete. As we will se when we discuss controllers, it can be an area where controllers are initialized (being that it is the top most DisplayObject in the display list).
Here is an example of a typical Document Class:
package { import flash.events.Event import com.gn.mvc2.App; import com.gn.mvc2.Config; import com.greensock.plugins.*; import com.greensock.TweenLite; import control.HelloController; public class HelloWorld extends App { /* --------------------------------------- */ /* --( PUBLIC PROPERTIES )-- */ /* --------------------------------------- */ public var helloController:HelloController; /* --------------------------------------- */ /* --( GLOBAL STATIC PROPERTIES )-- */ /* --------------------------------------- */ public static var config:Config; /* --------------------------------------- */ /* --( CONSTRUCTOR )-- */ /* --------------------------------------- */ public function HelloWorld() { __init(); } /* --------------------------------------- */ /* --( PRIVATE METHODS )-- */ /* --------------------------------------- */ private function __init():void { HelloWorld.config = new Config(this); preloader.addEventListener(Event.COMPLETE, __onPreloaderComplete); preloader.init(HelloWorld.config); TweenPlugin.activate([AutoAlphaPlugin]); } private function __onPreloaderComplete(event:Event):void { TweenLite.to(preloader, 0.5, {autoAlpha: 0, onComplete: function(){ gotoAndStop(2); }}); } /* --------------------------------------- */ /* --( PROTECTED OVERRIDES )-- */ /* --------------------------------------- */ override protected function _initControllers():void { helloController = new HelloController(this); } } }
Views
Views are handled a bit differently in the new approach. The main difference is we now use the Export/Linkage properties of the Flash Library to directly attach our Library assets to our view classes. See the below example:

Now that direct linkage is used to attach the view classes, we are able to treat these classes as members of the display list. The advantage is now any events dispatched from these classes can bubble up the display chain. If you notice in our example above of our Document Class, we instantiated our controller in that class. Being that it is the top most object in the display list, it will receive all events bubbling up from the views. The controllers basically listen to the class passed into their constructor (ie. new Controller(this) ), and the handlers within them interact with the models, or web services.
Here is an example of our view class:
package views { import flash.display.Sprite; import flash.events.MouseEvent; import events.CommandEvent; import events.HelloModelEvent; import model.ModelLocator; public class Main extends Sprite { /* --------------------------------------- */ /* --( CONSTRUCTOR )-- */ /* --------------------------------------- */ public function Main() { super(); _addListeners(); _setupBindings(); _setupChildren(); } /* --------------------------------------- */ /* --( PROTECTED METHODS )-- */ /* --------------------------------------- */ /** * Setup initial state of children */ protected function _setupChildren():void { // This is an example of using the global config object helloMessage_txt.text = HelloWorld.config.data.defaultMessage; } /** * Add event listeners to objects */ protected function _addListeners():void { submit.addEventListener(MouseEvent.CLICK, _onSubmitClick); } /** * Setup model bindings */ protected function _setupBindings():void { ModelLocator.instance.helloModel.addEventListener( HelloModelEvent.MESSAGE_UPDATED, _onMessageUpdate ) } /** * Submit button event handler */ protected function _onSubmitClick(event:MouseEvent):void { if (textInput.text.length > 0) { dispatchEvent(new CommandEvent(CommandEvent.SUBMIT_INPUT, { message: textInput.text })) } } /** * Model Binding Handler: When helloMessage is update in model */ protected function _onMessageUpdate(event:HelloModelEvent):void { helloMessage_txt.text = event.message; } } }
You'll notice that when our submit button is pressed an event is dispatched which will then bubble up to the controller
Controllers
You can think of Controllers in this framework as little functionality branches of the display tree. They listen to events coming from the DisplayObject they are instantiated from, and any of that DisplayObject's children. This allows you to place the controller wherever you need to in the application architecture to create an easy to read, modularized application.
Here is a controller:
package control { import flash.display.DisplayObject; import com.gn.mvc2.Controller; import events.CommandEvent; import model.ModelLocator; import model.HelloModel; public class HelloController extends Controller { /* --------------------------------------- */ /* --( PROTECTED PROPERTIES )-- */ /* --------------------------------------- */ // Models used by controller protected var _helloModel:HelloModel; /* --------------------------------------- */ /* --( CONSTRUCTOR )-- */ /* --------------------------------------- */ public function HelloController(parent:DisplayObject) { super(parent); _helloModel = ModelLocator.instance.helloModel; _setupCommands(); } /* --------------------------------------- */ /* --( PROTECTED METHODS )-- */ /* --------------------------------------- */ /** * Setup controller commands */ protected function _setupCommands():void { addListener(CommandEvent.SUBMIT_INPUT, _onSubmitInput); } /** * On SUBMIT_INPUT Command Event */ protected function _onSubmitInput(event:CommandEvent):void { if (event.data.message) { _helloModel.helloMessage = event.data.message; } } } }
To elaborate on this, say you have an application that is made up of 5 different "sections". Each section is its own View class. You could make it so that each one of those view classes instantiates it's own controller, and that controller only handles events/commands coming from that View and its children
This is handled by passing the parent display object in as a property of the constructor and storing it as a property of the Controller. I then run an init function (called _setupCommands in the above example) which sets up the listeners on the parent DisplayObject
In the above example, I have abstracted the storage of the parent DisplayObject into a property of a Class called Controller, which I extend all my controllers from. It has a helper function called 'addListener' which simply adds a listener to the parent DisplayObject
Models
Models are still dirt simple. They are basic classes that extend EventDispatcher and are used to hold state and data for the application. When Models are updated they should fire off an event that tells anyone listening that they have been updated.
The main difference is that we now implement a ModelLocator pattern (borrowed from Cairngorm and other such frameworks) to instantiate and gain access to the models from the views and controllers.
Here is an example of a ModelLocator:
package model { import flash.events.EventDispatcher; public class ModelLocator extends EventDispatcher { /* --------------------------------------- */ /* --( PUBLIC PROPERTIES )-- */ /* --------------------------------------- */ // Models public var helloModel:HelloModel = new HelloModel(); /* --------------------------------------- */ /* --( STATIC PROPERTIES )-- */ /* --------------------------------------- */ private static var _instance:ModelLocator; /* --------------------------------------- */ /* --( STATIC READ-ONLY PROPERTIES )-- */ /* --------------------------------------- */ public static function get instance():ModelLocator { return initialize(); } /* --------------------------------------- */ /* --( STATIC PUBLIC METHODS )-- */ /* --------------------------------------- */ public static function initialize():ModelLocator { if (_instance == null){ _instance = new ModelLocator(); } return _instance; } /* --------------------------------------- */ /* --( CONSTRUCTOR )-- */ /* --------------------------------------- */ public function ModelLocator() { super(); if( _instance != null ) throw new Error( "Error:ModelLocator already initialised." ); if( _instance == null ) _instance = this; } } }
The ModelLocator uses the singleton pattern so that these models can be accessed from anywhere in the application. The Models themselves are created as properties of the ModelLocator, and are instantiated at the same time the ModelLocator instance is instantiated.
Here is a typical Model:
package model { import flash.events.EventDispatcher; import events.HelloModelEvent; public class HelloModel extends EventDispatcher { /* --------------------------------------- */ /* --( READ/WRITE PROPERTIES )-- */ /* --------------------------------------- */ public function get helloMessage():String{return __helloMessage;} public function set helloMessage(value:String):void { __helloMessage = value; var modelEvent:HelloModelEvent = new HelloModelEvent( HelloModelEvent.MESSAGE_UPDATED ); modelEvent.message = __helloMessage; dispatchEvent(modelEvent); } private var __helloMessage:String /* --------------------------------------- */ /* --( CONSTRUCTOR )-- */ /* --------------------------------------- */ public function HelloModel() { super(); } } }
Wrap it all up
That is the basics of how I am implementing the MVC micro-architecture in my projects these days. So far it has covered all of the bases for the projects I work on. I'm interested to know how you guys implement what I've detailed, or any other implementations you may have yourself.
The source code for the HelloWorld application that I used as examples for this post can be downloaded here:
**SPECIAL NOTE** - This HelloWorld App will not compile. It was built with some dependencies on a framework that we use here at work, which I am not able to publish. None of these classes change the the architecture of this MVC implementation, and it would be easy enough to remove these and everything would function the same. I left these in to provide some clues as to ways you would be able to implement global variables using static properties in the DocClass, and how we handle listening for preloaders and setting up config objects. I may not be able to give you any code for these, but I can answer questions if any of you have them.


Comments
Just wrote a comment, but I guess it needs to be approved before it is shown here.
In the meantime, I have run across an issue. You say that we can remove references to the dependency classes without a problem. But it looks like the Controller class that HelloController is extending has an important function:
addListener(CommandEvent.SUBMIT_INPUT, _onSubmitInput);
Any chance you can shed some light on what this function does?
Thanks!
Basically, it said, "thanks for the great blog posts about Flash and MVC"
:-)
I'm happy with controllers and views having links to models, and think that doing that is a more fine-grained approach to maintaining them than using a model locator, which is a humongous global variable with a hollywood name.
I like the ability to have new controllers in the new approach, but the only functionality they seem to add here is the ability to neaten your code by separating it into multiple classes. This could have been done in other ways anyway, without calling all of the other classes controllers. The controllers here do not add the same benefits as multiple controllers do in other scenarios, such as server-side web development, since your inputs are events rather than URLs and arguments.
Also I don't know why you needed to export the views from the FLA. Why is not enough just to extend from Sprite, MovieClip or DisplayObjectContainer in code?
All this said, thanks very much for posting this. Keep posting. :) It made me think and it may be that if you reply to my comment it will prove me wrong on several counts and help broaden my knowledge of MVC.
For example, if you have a view, that makes lots of direct calls to a controller, and then want to re-use that view, but change the way the controller is structured, you end up having to strip out all of those direct calls. Where as, if the view is simply firing off events, and the controller is listening for those events to bubble up the display list, you can completely change the way the Controller is structured, it's business logic, whatever. The view is completely decoupled from the controller. The view doesn't even need to know that a controller exists, or what it's called. It is simply firing off events, that some other class listens for.
In regards to using the Export feature of the IDE, this just makes more sense for so many reasons. Think of it like this, when an object is created and added to the stage by the IDE, that object, and all it's functionality should be immediately available. Well, using the decorator pattern that the last approach used, that's not the case. Some other line of code somewhere in the app has to add the graphical parts of the object to the decorator. What I ran into with this is:
Say you have parts of the object/view that are not supposed to be seen in the initial display state of the object. Or, you need to programmatically decide based on some other data in the app what is shown. The problem with a decorator is that the constructor for the asset package has already ran (and may already have been added to stage), when the constructor for the decorator runs. So even if you change the display state in the decorators constructor, there may be enough of a delay between the two objects constructing that you see a frame of assets that shouldn't be seen for the current state.
This approch encourages componentization. Since starting to use this approch, I have found that more and more, them components I use for one project, can easily be moved to another project without doing anything more than reskinning in the IDE.
Another benefit to loose coupling is it allows multiple developers to work together without having this know the exact API's if the Controllers and Models. So you can have one developer working on the view code, firing off events when interactions occur, while another developer is building the controller logic, which gets triggered by those events.
I hope that gives you some more clairty on why I've moved towards this approch to MVC. The transition came after writing dozens and dozens of apps, and hundreds of thousands lines of code, so it's sometimes difficult to explain all the reasons why I find this MVC structure more reliable and favorable.
I think that your approach probably incorporates solutions to things you have encountered while developing over time, which I can't imagine unless I actually have a go at incorporating it myself. So I'll do that and I'm sure the reasons will become clearer to me. At the moment I'm probably having a failure of imagination.
Those two helper functions are addListener and removeListener which basically add and remove a listener to the parent DisplayObject that is passed into the constructor. The method signatures are identical to the the addEventListener and removeEventListener methods in the Flash API.
So rather than having to type 'parent.addEventListener' or 'parent.removeEventListener' for every command, I can just do 'addListener' and 'removeListener'. Saves the fingers some typing :)
Other than that great work - I'm currently working this example through, its my first MVC attempt and having found several MVC examples in the web this one makes the best impression!
Thank you!
Let me pick an example: an image view. This ImageView would have a progress bar and a container for a bitmap. When ImageView is ready to load the bitmap it would fire an event to ImageController asking for it to load the image. ImageController would then try to pass the progress information somehow (?) to the ImageView. When image is completely loaded, ImageController would tell ImageModel to store the data and tell ImageView that it is ready to show.
Is that correct? If so, wouldn't it be much easier to just add progress and complete listeners right inside ImageView?
I appreciate if you can help me understand that.
I think breaking up your components into smaller chunks and having an MVC for each component might be a better option if you are worried about reuse. But I have seen this go way too far to the point where there is more "structure code" in a class than "action code".
rp
import com.gn.mvc2.Config;
I am interested to know about these two classes which you not discussed here. Will you please attached in your example file?
Or even show the classes so that I can able you run this approach.
Thank You
Regards
FFA
Leave a Comment