PureMVC VS RobetLegs match-up!
Then you start creating bigger applications, or start working in bigger teams – using good framework becomes essential.
I spent good amount of time with 2 currently most popular AS3 frameworks – PureMVC and RobotLegs, and here I will compare pros and cons of both.
Generally both frameworks are very similar! :
RobotLegs and PureMVC Pros:
- will help divide your code is small units and wire them;
- will help standardize your code;
- based on MVC;
- can be extended to your needs;
- makes you focus on your fun app instead of focusing on solving architecture puzzles;
- have big and active communities;
RobotLegs and PureMVC Cons:
- hurts performance a bit;
- harder to debug “black box” framework code;
- couples implementation with framework;
…but RobotLegs:
- supports injection.(produces unit testable code almost by default..)
- don’t use singletons.
- full modular support as it is…(no special classes to switch to multicore…)
- supports option to use presentation pattern out of the box, not only MVC. (it’s good for small view oriented apps.).
- code ends up cleaner and smaller.
- supports weak event listeners by default.
- solves flex view object initiation nightmares, then view is not usable yet for manipulations.
- is still being improved. (version 2.0 is coming..)
… but PureMVC:
- is ported to many other programming languages.
- more matured. Have longer history.
- bigger community.
How those frameworks gets things done:
| RobotLegs | PureMVC | |
| Getting objects | Uses automatic dependency injection. | Uses static Facade class to give you ability to retrieve objects you need. |
| Communication | Flash event based system. (can be switched to signals) | Uses custom Notification system. |
Framework comparison:
I created pseudocode examlpes for both frameworks that does the same thing. I covered all core framework features you will be using most of the time.
I used same comments in both examples so you could compare how use cases looks like in code.
Base clases:
PureMVC : Facade.as
RobotLegs : Context.as
package { import org.puremvc.as3.interfaces.IFacade; import org.puremvc.as3.patterns.facade.Facade; public class PureMvcFacade extends Facade implements IFacade { private static const STARTUP:String = "startup"; public static function getInstance():PureMvcFacade { if (instance == null) instance = new PureMvcFacade(); return instance as PureMvcFacade; } override protected function initializeController():void { super.initializeController(); // map commands registerCommand(STARTUP, StartupCommand); } override protected function initializeModel():void { super.initializeModel(); // map model facade.registerProxy(new DataProxy()); } override protected function initializeView():void { super.initializeView(); // map view facade.registerMediator(new GameMediator(new GameSprite())); } public function startUp(stage:Stage):void { // start app... sendNotification(STARTUP, stage); removeCommand(STARTUP); } } } |
VS
package { import flash.display.DisplayObjectContainer; import org.robotlegs.mvcs.Context; public class RobotLegsContext extends Context { public function RobotLegsContext(contextView:DisplayObjectContainer){ super(contextView); } override public function startup():void { // map commands commandMap.mapEvent(ContextEvent.STARTUP_COMPLETE, StartupCommand, ContextEvent); // map model (and services) injector.mapSingleton(DataModel); // map view mediatorMap.mapView(GameSprite, GameMediator); // start app... super.startup(); } } } |
Both:
- initiate framework
- is used to map commands to triggers.
- is used to declare/initiate model(services) and mediators+views or executes commands to do it.
These classes has a bit different framework role:
- PureMVC Facade main role is provide you with application parts.
- RobotLegs Context main role is to act as middle man to let application parts to communicate.
Commands:
package { import org.puremvc.as3.patterns.command.SimpleCommand; public class PureMvcCommand extends SimpleCommand { override public function execute(notice:INotification):void { // geting command parameters var params:DemoVO = notice.getBody() as DemoVO; // get data var dataProxy:DataProxy = facade.retrieveProxy(DataProxy.NAME) as DataProxy; // get mediator var testMediator:TestMediator = facade.retrieveMediator(TestMediator.NAME) as TestMediator; // execute enother command. sendNotification(Note.ANOTHER_COMMAND, new CommandParamVO()); // do stuff... } } } |
VS
package { import org.robotlegs.mvcs.Command; public class RobotLegsCommand extends Command { // geting command parameters [Inject] public var event:DemoEvent; // get data [Inject] public var dataModel:DataModel; // get mediator // NOT POSSIBLE ! You send events to alter view. override public function execute():void { // execute enother command. commandMap.execute(AnotherCommand, new AnotherCommandEvent()); // do stuff... } } } |
Pure MVC and RobotLegs commands does the same thing – they hold business logic. BUT:
- In RobotLegs you write less.
- RobotLegs does not support getting mediators in commands. (it helps avoid code that does not belong to commands)
- PureMVC has special class for MacroCommand. RobotLegs does not need it as it can do the same in generic Command
Model:
package { import org.puremvc.as3.interfaces.IProxy; import org.puremvc.as3.patterns.proxy.Proxy; public class PureMvcProxy extends Proxy implements IProxy { public static const NAME:String = "PureMvcProxy"; public function PureMvcProxy(){ // init dataVO object or get it as constructor parameter... var proxydata:DataVO = new DataVO(); super(NAME, proxydata); } // get and cast data object for convenience.. public function get dataVo():DataVO { return super.getData() as DataVO; } public function doStuff():void { // do stuff.. // send triger.. sendNotification(DataNote.STUFF_DONE, new DataChangePoramsVO(stuffParam1, stuffParam2)); } } } |
VS
package { import org.robotlegs.mvcs.Actor; public class RobotLegsModel extends Actor { private var dataVO:DataVO; public function RobotLegsModel() { // init dataVO object or get it as constructor parameter... dataVO = new DataVO(): } public function doStuff():void { // do stuff.. // send triger.. dispatch(new DataChangeEvent(DataChangeEvent.STUFF_DONE, stuffParam1, stuffParam2)); } } } |
Pure MVC and RobotLegs models does the same thing, they both provide API to get/set/manipulate your data. BUT:
- RobotLegs implementation is simple and straight-forward, it requires way less code, and it can be extended to whatever you need.
- PureMVC need to send you data object to super class, and cast it for local use – annoys, and in most cases is not needed. (I usually don’t bother and store it in local variable)
- In RobotLegs you have couple of extra options then it comes to Models because of Injector that handles it. You can construct model class with Command and map it, or let Injector automatically instantiate it with first use.. You can even tell Injector automatically create new instances of Model class every time you need it – you get a simple Model object factory.
Also worth mentioning:
- Both frameworks supports multi instances of same Model, with help of extra type parameter.
- RobotLegs advices using Services for handling asynchronous and remote data, while PureMVC advices to us RemoteProxy. But that is only concepts, both don’t have special framework classes or code.
- Both RobotLegs and PrueMVC allows to use interfaces to help manage data access.
Mediator+View:
package { import org.puremvc.as3.interfaces.IMediator; import org.puremvc.as3.interfaces.INotification; import org.puremvc.as3.patterns.mediator.Mediator; public class PureMvcMediator extends Mediator implements IMediator { public static const NAME:String = "PureMvcMediator"; public function PureMvcMediator(initViewComponent:ViewComponent){ // init view component super(NAME, initViewComponent); } override public function onRegister():void { // init view, prepare mediator for work... // listen for view events viewComponent.addEventListener(MouseEvent.CLICK, handleViewClick, false, 0, true); } override public function onRemove():void { // dispose view, clean up mediator... } // cast view for convenient local use. public function get viewComponent():ViewComponent { return super.getViewComponent() as ViewComponent; } // listen for framework notices override public function listNotificationInterests():Array { return [ // DataNote.STUFF_DONE // ]; } // handle framework events override public function handleNotification(notice:INotification):void { switch (notice.getName()){ case DataNote.STUFF_DONE: var dataChangePoramsVO:DataChangePoramsVO = notice.getBody() as DataChangePoramsVO; viewComponent.showStuff(dataChangePoramsVO.stuffParam1); break; default: trace(" WARNING : PureMvcMediator does not handle notificotion:", notice.getName()); break; } } // handle view events private function handleViewClick(event:MouseEvent):void { sendNotification(ViewNote.VIEW_CLICKED, new ViewParamVO(viewParam1, viewParam2)); } } } |
VS
package { import flash.events.Event; import org.robotlegs.mvcs.Mediator; public class RobotLegsMediator extends Mediator { // init view component [Inject] public var view:ViewComponent; override public function onRegister():void { // init view, prepare mediator for work... // listen for view events addViewListener(MouseEvent.CLICK, handleViewClick); // listen for framework events addContextListener(DataChangeEvent.STUFF_DONE, handleStuffDone) } override public function onRemove():void { // dispose view, clean up mediator... } // handle view events private function handleViewClick(event:MouseEvent):void { dispatch(new ViewComponontEvent(ViewComponontEvent.VIEW_CLICKED, viewParam1, viewParam2)); } // handle framework events private function handleStuffDone(event:DataChangeEvent):void { viewComponent.showStuff(event.stuffParam1); } } } |
PureMVC and RobotLegs mediators does the same thing: they both mediate the view – acts as communication bridge between view and framework. BUT:
- in RobotLegs you write much less.. and it’s more intuitive to learn.
- in RobotLegs you don’t have to write endless switch-case statements to listen and handle all notes that comes in.
- in RobotLegs you do have a problem if you have huge amount of view object on stage with auto Injection mechanism performance. But you can fix it by making injection lazy.
- PureMVC need to send you view object to super class, and cast it for local use – annoys, and in most cases is not needed. (I usually don’t bother and store it in local variable)
Communications:
Both frameworks has different communication mechanisms, and examples are already shown above.
Function wise both approaches gets the job done well.
Performance wise RobotLegs Events has some overhead, but if you have that huge application – you can easily fix it by changing Events to Signals.
Verdict:
RobotLegs is the framework that delivers everything that PureMVC does… BUT:
- in cleaner, more intuitive way, you have to write less in RobotLegs. (even if you are smart and use snippets and templates to automate PureMVC classes and object retrieval – you still benefit from smaller code base. )
- RobotLegs is better suited for Test Driven Development.
PureMVC is more matured framework, and has bigger community, but RobotLeg popularity is growing rapidly, and RobotLegs is still being improved! Next version(2.0) of framework is coming soon. (Check out this presentation!)
In the end.. you don’t use framework that has largest google search hits… you use framework that suits your project needs bests, and that gives you most enjoyable programming experience.
There is no doubt that both are GREAT frameworks for writing big applications, and will definitely do the job of improving your productivity.
But if I can choose to improve readability and structure of my code, and make code better testable – the choice is no brainier for me.
Couple of thoughts on this article. First of all, the less code argument is redundant as you write your code using templates. If its robotlegs or pureMVC, you create a new object from a template. Also the point of unit testing, in my 10 years of experience in web marketing, i wrote a unit test only three times. The reason behind it, it is more simple to compile, run and see in flash than write a unit test for every object you wish to test.