...ndid, the somewhat small but happy love child of Nicolás Delfino. A company specialized in interactive websites and rich media applications, knee deep in actionscript and down with jazz...


AS3 MVC Example and Analogy – revised
Posted on: December 3rd, 2010

These last couple of weeks I´ve been struggling at home trying to learn the mvc design pattern. The problem for me when learning new things, specially code related, is that the logic engine of my brain usually shuts off at the beginning of my journey, blocking the way of all common sence needed to explore unknown territories and instead gets replaced with an unhealthy dose of obsession.

That being said, what happend was that I started reading tons of blog posts and articles about mvc, began staying up all night, downloaded every example I could find and drank to much coffee until I got to a point where I´d managed to write a simple application without having a single clue of How and specially Why it all worked…

So there I was, dressed in a stinky wife-beater t-shirt staring at my monitors when a mail delivery (door mail) made me remember two things I´d totally forgoten; namely, that prior to the awesome week I just talked about I´d ordered a book called Actionscript 3.0 Design Patterns, I had also joined lynda.com as a monthly subscriber (thanks mom).

This was exactly what worked for me, what I needed in order to get back some of that common sence I´d lost.

The lynda.com oop training cource is a great resource for newly wedded as3 developers wanting to learn how to write smarter code, and the first couple of hours are great for cathing up on stuff (though it gets more complex along the way). And though I nowadays feel more at ease writing prettier code, it was hours well spent.

Actionscript 3.0 Design Patterns is my number one go to reference when in doubt, just a great book with a lot of common sence and a huge collection of different design patterns ( yes there´s plenty of them – totally freaked when I first opened it but more on that in another post…)

Long story I know but that certain combination of video and hard copy really helped me understand the mvc pattern for what it really is, wich now leads me to the whole purpose of this blog post.

MVC Analogy

I decided that from now on when I face a problem that keeps me up all night I´m going to try to write an example and post it on my blog, …well not Every single time ( I´d had enough material for a book if I did )

1. The Model aka “Blind Mr Miyagi”:

So the easiest way of understanding mvc (wich btw stands for model, view, controller) is to look at it as if there were three guys sitting in seperate rooms:

The first guy, the model is like an awesome karate master, and blind. Like a very wise and blind Mr Miyagi that speaks (well shouts actually) and acts only when it´s absolutely necessary…

A kind of a know-it-all that holds all the juicy information regarding the state of your application.

Though open minded, when it comes to changes in his department he only listens to one man – Hagrid the Groundskeeper (the controller, more on why later…)

2. The View aka “Perez Hilton”:

The second guy, the view is like an artist with serious needs of attention, particularly from Mr Miyagi. He is social in nature and feeds from everything that happens around him, and though he might sound annoying, he´s good little trooper who loves to work!

Besides giving the ocacional poke to Hagrid (his younger brother whom we still haven´t talked about) he´s also the one in charge of everything visual in your application, constantly listening for gossip from Mr Mijagi (the model) so that he´ll get to do what he does best – update the screen

3. The Controller aka “Hagrid the groundskeeper”:

Last but not least we have the third guy, the controller. A loving character that´s kind of all over the place, interacts with everyone, strong but humble with a huge respect for the chain of command.

In his world Perez Hilton´s word is final (family rules you know), has leverage over Mr Miyagi somehow and gets to tell him what to do

Hagrid´s role as the younger brother means hard work as he is also responsible for instantiating the View (Perez) and initiating the Model (Mr Miyagi), telling the Model that it´s time to wake up!

Basic MVC example

This example is a simple and in some ways useless application that does this:

From a user´s point of view:

  1. It tells you to click somewhere on stage
  2. The square moves to where you last clicked and the trace textfield tells you the exact x and y values

Behind the scenes:

  1. It all starts with Hagrid instantiating Perez and initiating Mr Miyagi.
  2. Miyagi now aware of Perez´s existence gets all his shit together (data) and then shouts “Ok Perez I´m good to go!”
  3. Newly born Perez Hilton, bored out of his mind now starts listening for a mouse click
  4. When the user clicks somewhere on the stage Perez goes nutz and sets the x and y properties of his younger brother Hagrid via a setter method, then he goes for a walk.
  5. Hagrid says “ok fine”, alters his private x and y values and walkes over to Mr Miyagi, to whom he does the same thing that Perez did only now it´s everything but civilized, he punches Miyagi in the face and sets his x and y´s.
  6. Mr Miyagi, in chock from the brutal bashing he just received experiences difficulties with his voice (Hagrid has huge fists and must´ve covered a big part of Miyagis´s throat, perhaps even his entire body). Luckily for us Mr Miyagi comes from the ancient family called Event Dispatchers meaning everyone in his family receives a super cool megaphone capable of dispatching information to whoever that´s listening (when they´re of age of cource).
  7. Miyagi walks over to his update method (like a speaker podium), picks up his megaphone and starts ranting “my x and y´s have been altered! check out my new values!” over and over again…
  8. Perez, coming home from his walk hears Mr Miyagi´s rantings and runs straight over to the little squared sprite wich he magically drew in the beginning of the application when it all started.
  9. He picks up the sprite, walks over to his sprite moving machine (the sprite_moveHandler), checks Miyagi´s values and ever so gently puts it down on its new location.

The main document class (mum):

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import mvc.move.controller.Controller;
	import mvc.move.model.Model;
	import mvc.move.view.TraceView;
	import mvc.move.view.View;

	[SWF(width = "635", height = "300", frameRate = "30", backgroundColor = "#D5EAFF")]
	public class Main extends Sprite
	{
		public function Main():void
		{
			this.addEventListener(Event.ADDED_TO_STAGE, init);
		}

		private function init(e:Event):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);

			var model:Model = new Model();
			var controller:Controller = new Controller(model, stage);
		}
	}
}

The Model (Mr Miyagi):

package mvc.move.model
{
	import flash.events.Event;
	import flash.events.EventDispatcher;

	public class Model extends EventDispatcher
	{
		private var _currentX:Number;
		private var _currentY:Number;

		public static const MODEL_INIT:String = "modelInit";
		public static const SPRITE_MOVE:String = "spriteMove";

		public function Model()
		{
		}

		public function init():void
		{
			// get data here
			dispatchEvent(new Event(MODEL_INIT));
		}

		private function update():void
		{
			dispatchEvent(new Event(SPRITE_MOVE));
		}

		public function get currentX():Number { return _currentX; }

		public function set currentX(value:Number):void
		{
			_currentX = value;

			update();
		}

		public function get currentY():Number { return _currentY; }

		public function set currentY(value:Number):void
		{
			_currentY = value;
			update();
		}
	}
}

The View (Perez Hilton):

package mvc.move.view
{
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import mvc.move.controller.Controller;
	import mvc.move.model.Model;

	public class View extends Sprite implements IView
	{
		private var _model:Model;
		private var _controller:Controller;
		private var _target:Stage;

		private var _sprite:Sprite = new Sprite();

		public function View(model:Model, controller:Controller, target:Stage)
		{
			_model = model;
			_controller = controller;
			_target = target;

			addSprite();

			_model.addEventListener(Model.MODEL_INIT, modelInit);
		}

		public function modelInit(e:Event):void
		{
			_model.addEventListener(Model.SPRITE_MOVE, sprite_moveHandler);
			_target.addEventListener(MouseEvent.CLICK, mouse_clickHandler);
		}

		public function sprite_moveHandler(e:Event):void
		{
			_sprite.x = _model.currentX;
			_sprite.y = _model.currentY;
		}

		private function mouse_clickHandler(e:MouseEvent):void
		{
			trace("x: " + mouseX + " y: " + mouseY);
			_controller.newX = mouseX;
			_controller.newY = mouseY;
		}

		private function addSprite():void
		{
			_sprite.graphics.beginFill(0x95A0B5, 1);
			_sprite.graphics.drawRect(0, 0, 20, 20);
			_sprite.graphics.endFill();
			_sprite.x = _target.stageWidth / 2 + 10;
			_sprite.y = _target.stageHeight / 2 + 10;
			_target.addChild(_sprite);
		}
	}
}

The Controller (Hagrid):

package mvc.move.controller
{
	import flash.display.Stage;
	import flash.events.Event;
	import mvc.move.model.Model;
	import mvc.move.view.IView;
	import mvc.move.view.TraceView;
	import mvc.move.view.View;

	public class Controller
	{
		private var _model:Model;
		private var _newX:Number;
		private var _newY:Number;
		private var _stage:Stage;

		public var view:IView;
		public var traceView:IView;

		public function Controller(model:Model, stage:Stage)
		{
			_model = model;
			_stage = stage;

			view = new View(_model, this, _stage);
			traceView = new TraceView(_model, this, _stage);

			model.init();
		}

		public function set newX(value:Number):void
		{
			_newX = value;
			_model.currentX = _newX;
		}

		public function set newY(value:Number):void
		{
			_newY = value;
			_model.currentY = _newY;
		}
	}
}

The TraceView:

package mvc.move.view
{
	import flash.events.*;
	import flash.display.*;
	import flash.events.Event;
	import flash.text.AntiAliasType;
	import flash.text.GridFitType;
	import flash.text.TextField;
	import flash.text.TextFieldType;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
	import mvc.move.controller.Controller;
	import mvc.move.model.Model;

	public class TraceView extends Sprite implements IView
	{
		private var _model:Model;
		private var _controller:Controller;

		private var _traceDisplay:TextField = new TextField();
		private var _target:Stage;

		public function TraceView(model:Model, controller:Controller, target:Stage)
		{
			_model = model;
			_controller = controller;
			_target = target;

			_model.addEventListener(Model.MODEL_INIT, modelInit);
			_model.addEventListener(Model.SPRITE_MOVE, sprite_moveHandler);
		}

		public function modelInit(e:Event):void
		{
			addTraceField();
			_model.addEventListener(Model.SPRITE_MOVE, sprite_moveHandler);
		}

		public function sprite_moveHandler(e:Event):void
		{
			_traceDisplay.text = "X: " + _model.currentX + " Y: " + _model.currentY;
		}		

		private function addTraceField():void
		{
			// input and display text:
			var t_textFormat:TextFormat = new TextFormat();
			t_textFormat.align = TextFormatAlign.LEFT;
			t_textFormat.size = 10;
			t_textFormat.bold = true;
			t_textFormat.font = "Arial";
			t_textFormat.leftMargin = 10;

			// text display:
			_traceDisplay = new TextField();
			_traceDisplay.defaultTextFormat = t_textFormat;
			_traceDisplay.width = 200;
			_traceDisplay.height = 16;
			_traceDisplay.selectable = false;
			_traceDisplay.textColor = 0x796753;
			_traceDisplay.type = TextFieldType.DYNAMIC;
			_traceDisplay.x = _target.stageWidth / 2 - _traceDisplay.width / 2;
			_traceDisplay.y = _target.stageHeight / 2 - 30;
			_traceDisplay.border = true;
			_traceDisplay.borderColor = 0xD3C9BE;
			_traceDisplay.text = "CLICK ON STAGE";
			_traceDisplay.multiline = true;
			_traceDisplay.wordWrap = true;
			_traceDisplay.background = true;
			_traceDisplay.backgroundColor = 0xDFD7D0;

			_target.addChild(_traceDisplay);
		}
	}
}

The IView Interface:

package mvc.move.view
{
	import flash.events.Event;

	public interface IView
	{
		function modelInit(e:Event):void

		function sprite_moveHandler(e:Event):void
	}

}
Tags: , ,

15 Responses

  1. (still haven’t read the article. I may have “on topic” thoughts then, but I have bookmarked it for tonight when I have more time)

    Which one was that was that Lynda video you were referring to?

  2. rosada says:

    Buenísimo pichón!!
    Super pedagógico y con el mismo sentido del humor que te aparece cuando te reís con tu risa tan especial.

    beso

  3. michael says:

    nice. just a thing:
    the model gets and provides data for the view (from a xml file, amf/remoting/php, …) which can take up to some seconds. the/a view/s usually depend on the model’s data – the model needs to tell the view/s when it’s ready. otherwise the view would run without having the model’s data = error. the way I learned it, the controller’s constructor instanciates the view (new View(..), after that the controller calls model.init(); (model does get data stuff here). the view listens to Model.INIT (static const). when model.init() function is done it dispatches Model.INIT to the view and the app can start.
    (no interfaces? http://www.zedia.net/2008/how-to-use-interface-in-actionscript-3/ )

    P.S.:
    checkout my opensource EventListener #as3 project:
    http://code.google.com/p/as3listenermanager

    • nicolasdelfino says:

      Hi Michael!
      Makes sence letting the controller instantiate the view/s and then initiating the model, I´ll definitly post an update to the code and make an IView interface for the views! thanks!

      • michael says:

        —-
        Main.as :
        model = new Model;
        controller = new Controller(model);
        addChild(controller); // controller extends Sprite

        Controller.as :
        model = m;
        view = new View();
        view.init(this, m);
        addChild(view);

        View.as::init() :
        controller = c;
        model = m;
        setModelListeners(); // listen to Model.INIT

        IView.as :
        function init(c:Controller, m:Model):void;

        There are more ways to do it… I looked into those as3 frameworks like parsley, cairngom and puremvc. All these are complicated but I decided that puremvc is probably the best for me, when I can make the time to learn it.

        cheers

  4. Hanna says:

    that’s a great analogy!

  5. Very nice read. :)

    Quick question on #2, is Mr. Miyagi aware of Perez (meaning has a reference to him in any way) or does Mr. Miyagi just call out “Okay, I´m good to go!” to whoever may be listening, not actually aware of Perez existence or even know who he is?

    • nicolasdelfino says:

      Thanks Andreas! As I undertand it, Mr Miyagi doesnt need to know anything about Perez Hilton nor needs a reference, and only shouts hoping that someone´s listening.
      Like a small indie radio station hoping to get through to new listeners maybe :)

  6. One more thing, have you ever worked with PureMVC? It adds a few extra classes to the mix, and I’m having a slightly tricky time getting my head around all items involved, and I would really like an explanation like this applied to PureMVC (as it seems that “dry” is a major requirement for all PureMVC tutorials, or at least the videos – I wish I was kidding…)

    I wish more tutorials were illustrated like this as it helps (at least me) to learn new concepts, as well as better remember the information.

    • nicolasdelfino says:

      Thanks for the kind words!

      this is actually my first time writing anything like this and I suppose I did it to learn more about mvc, I also think that verbalizing what you think you know sometimes helpes to really dig deep into the subject. Using odd personalities instead of abstract names helps me push away thoughts as “this is too hard” and “why on earth is it called a controller when…” – you know…

      Now regarding pureMVC I haven´t even touched the subject , , as you said frameworks ARE tricky to get your head around, specially by yourself, and the hardest thing is to grasp WHY you should use it. After you get the WHY I guess it becomes easier having a specific goal knowing why you´re learning.

      Any tips for getting started with pureMVC?

      Cheers!
      Nicolas

  7. Marc says:

    Hey!

    Great example, i was looking for something like this :)

    thx a lot!

Leave a Reply to Andreas Renberg