ThinkGeek - Cool Stuff for Geeks and Technophiles

March 01, 2004

Design patterns: almost MVC ( model-view-controller )

We'll build a reverse clock. The clock starts when the stage is clicked, and can be stopped clicking the stage again.

To read a really good article about what the MVC pattern is, click here

Basically, we'll try to break our application into three layers. The model (application data and logic ), the view ( the interface) and the controller ( responsible of updating the data when the interface changes, and responsible of updating the interface when the data changes ).

In this example, the model is a AS2 class, that will count the remaining time. With every second, this class fires an event, that is listened by the controller, that will update the view, which is a textfield that shows the remaining time. When we click the stage, the view tells de controller to change it's state, and the controller will tells the model to do so ( to stop if it was running, or to run if it was stopped ).

First, you must be sure that swf loading order ( File-> Publish settings-> swf-> loading order ) is set to "down to up". So, the controller will be created on the first frame of the first layer, and the view will be created in the first frame of the second layer.

To create the controller instance:

import net.designnation.patterns.*

var myController: controller = new controller( this );
myController.init( 60 );

The controller aggragates an instance of the model:

public function init( time: Number )
{
 this.myClock = new clock( this.timeline, time );
 	
 this.myClock.addEventListener( "endOfTime", this );
 this.myClock.addEventListener( "updateTime", this );
 	
 this.myClock.init( );
}

So, when the clock instance is created, it receives two parameters, a reference to _root, and the total time, so:

public function init( )
{
 this.clockClip = timeline.createEmptyMovieClip( "empty", 
                               this.timeline.getNextHighestDepth( ) );	
 this.clockClip[ "owner" ] = this;
 this.clockClip.onEnterFrame = function ( )
 {
  if ( this[ "owner" ].workingFlag )
  {
   if ( this[ "owner" ].actualTime>= 1 )
   {
    this[ "owner" ].actualTime--;
    if ( this[ "owner" ].actualTime % FRAMERATE == 0 )
    {
     this[ "owner" ].dispatchTime( this[ "owner"].actualTime );
    }
   }
   else
   {
    this[ "owner" ].dispatchTime( );
    delete this.onEnterFrame;
   }
  }
 }
}

When I write

this.clockClip[ "owner" ] = this;

it's exactly the same than:

this.clockClip.owner = this;

The brackets just help me see that it's a dynamically created variable.

When a second has passed, an event is fired:

private function dispatchTime( actualTime: Number )
{
 if ( actualTime == null )
 {
  var event = "endOfTime"; 
 }
 else
 {
  var event = "updateTime";
 }
 	
 this.dispatchEvent( {type: event, target: this, time: ( actualTime/ FRAMERATE ) } );
}

The controller is registered as a clock listener so the following method is executed:

public function updateTime( arg: Object )
{
 this.timeline.clock.text = arg.time;
}

so the view is updated.

When we click the stage:

this.onMouseUp = function( )
{
 myController.changeClockState( "clock" );
}

So, in the controller:

public function changeClockState( )
{
 this.myClock.changeClockState( );
}

And finally,

public function changeClockState( )
{
 this.workingFlag = !this.workingFlag;
}

So, every message between the view and the model is carried by the controller. So the graphics and the application logic are separated, so our application will be easier to maintain, our code will be easier to understand....

And again, we've mixed AS2 and AS1 code.

Download the source files here

Posted by Cesar Tardaguila Date: March 1, 2004 08:06 AM | TrackBack
Comments

The controller should not be listening to the model.. Instead, the view should listen to the Model and recieve the events when the model is updated so the view can update itself.

See the structure section here:
http://java.sun.com/blueprints/patterns/MVC-detailed.html

For a good AS1 implementation, check out this link:
http://chattyfig.figleaf.com/flashcoders-wiki/index.php?MVC

My example in AS2 (based on the above link) will be available shortly, on the MXDU website for my session, located here:
http://www.mxdu.com/go/agenda/client/object-oriented-programming-with-actionscript-2.0

Posted by: darron en: March 1, 2004 04:45 PM

Sorry, Darron, and sorry to everybody who's read this post.

These are the things that happen when you code too much and don't sleep.

I'll post a correction as soon as possible.

Anyway, I must admit that this is the implementation that I use in my daily work. And I've been using it for so long, that I have almost assumed it as the right one. Maybe I should have called this post " a short of MCV".

Thanks for your comment, Darron

Posted by: Cesar Tardaguila en: March 1, 2004 05:50 PM

Anyway, I've written what I hope will be a more accurate implementation here:

http://www.design-nation.net/en/archives/000156.php

Posted by: Cesar Tardaguila en: March 2, 2004 12:04 AM

Updated link to Darron's conference
http://www.mxdu.com/index.cfm?objectid=7B230849-D0B7-4CD6-F9A6011D47506F49

Posted by: Blip en: November 22, 2004 09:24 PM