
Truckin' thru MVC
Adding an Interface for the View
Before adding another view, I wanted to add an interface so that I could use the same Model and Controller classes. The changes are minimal in the revised View classes, and I changed the Client so that it could more easily request any view the user would like. Since you have all of the details about how the MVC works in Parts I-III, I’ll get right into the interface.
In looking at the View class in Part III, you can see that the class has only two methods. By putting them into an interface, we can program to the interface instead of the implementation. That means that as long as the same interface is used, you can use the same Model and Controller without any changes at all. As usual, interfaces are fairly short, and this one is no exception.
1 2 3 4 5 6 7 8 9 10 11 | package { import flash.display.Sprite; import flash.events.Event; public interface IView { function createView(vessel:Sprite):Sprite; function update(e:Event):void; } } |
Before continuing, click the Play button to test both compasses that have their roots in that interface. (compi?)
![]()
The Model and Controller classes are identical for both compass examples. Read on to see how easy it is to add views.
Because we now have an interface for the View, we need to update the original View class so that it implements the IView interface. The name of the View classes need to be unique; so the name has been changed from View to ViewAnalog. Likewise, the createAnalog() method has been given the more general name, createView() in line with the more general task of the method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | package { import fl.controls.Slider; import fl.events.SliderEvent; import flash.display.Sprite; import flash.events.Event; public class ViewAnalog implements IView { private var model:Model; private var controller:Controller; private var base:Sprite; private var compassRose:Sprite; private var pointer:Sprite; private var slider:Slider; public function ViewAnalog(model:Model, controller:Controller, vessel:Sprite) { this.model=model; this.controller=controller; base=createView(vessel); } public function createView(vessel:Sprite):Sprite { //add compass rose and needle compassRose=new CompassRose(); compassRose.x=250,compassRose.y=250; vessel.addChild(compassRose); pointer=new Pointer(); pointer.x=0,pointer.y=0; compassRose.addChild(pointer); //**add slider var slideHolder=new Sprite(); vessel.addChild(slideHolder); slider=new Slider(); slider.maximum=360; slider.width=slider.maximum; slideHolder.addChild(slider); slider.x=(250) - (slider.width/2); slider.y=20; slider.addEventListener(SliderEvent.CHANGE, controller.newDirection); return compassRose; } public function update(e:Event):void { pointer.rotation=model.getDir(); } } } |
As you can see, it’s almost identical to the original View class. Now for the payoff of loose coupling. Instead of an analog compass, this next one is a retro-digital compass. With a black background and green text, it looks like the old monitors with black screens and green output. Placed next to the analog compass, you can see the same exact data in a wholly different framework. Figure 1 compares the different views with the same data:

Figure 1: Adding new views is easy with MVC
The following listings shows the changes that were made. Note that instead of using MovieClip objects for display, this next view uses a Shape for the background and a TextField and TextFormat for the value state in the Model. However, both are placed in the vessel Sprite and work just fine. The same Slider with the same code is reused except instead of changing the rotation, the value is placed in a TextField object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | package { import fl.controls.Slider; import fl.events.SliderEvent; import flash.display.Sprite; import flash.events.Event; import flash.text.TextField; import flash.text.TextFormat; import flash.display.Shape; public class ViewRetro implements IView { private var model:Model; private var controller:Controller; private var base:Sprite; private var slider:Slider; //Instantiate text field and format private var txtFld:TextField=new TextField(); private var format:TextFormat=new TextFormat(); public function ViewRetro(model:Model, controller:Controller, vessel:Sprite) { this.model=model; this.controller=controller; base=createView(vessel); } public function createView(vessel:Sprite):Sprite { //Create backdrop using a Shape var crt:Shape=new Shape ; crt.graphics.beginFill(0x000000); crt.graphics.lineStyle(0,0x000000); crt.graphics.drawRoundRect(100,60,300,50,6); crt.graphics.endFill(); vessel.addChildAt(crt,0); format.color=0x00ff00; format.font="OCR A Std"; format.size=32; txtFld.defaultTextFormat=format; txtFld.x=220; txtFld.y=70; //Set default value to 0 so that //it does not display null--NaN txtFld.text=0; vessel.addChildAt(txtFld,1); //add slider var slideHolder=new Sprite(); vessel.addChild(slideHolder); slider=new Slider(); slider.maximum=360; slider.width=slider.maximum; slideHolder.addChild(slider); slider.x=(250) - (slider.width/2); slider.y=20; slider.addEventListener(SliderEvent.CHANGE, controller.newDirection); return vessel; } public function update(e:Event):void { txtFld.text=model.getDir(); } } } |
Finally, the Client has to be changed a bit so that selecting one view or another is simple to do. (You can get fancier if you like and set up a UI selection.) The following listing shows the Client.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | package { import flash.display.Sprite; public class Client extends Sprite { private var model:Model; private var view:IView; private var controller:Controller; public function Client() { setMVC("r"); } private function setMVC(viewType:String):void { model = new Model(); controller=new Controller(model); switch (viewType) { case "a" : view=new ViewAnalog(model,controller,root); break; case "r" : view=new ViewRetro(model,controller,root); } model.addEventListener(Model.UPDATE, view.update); } } } |
As far as I can see, the MVC fulfills exactly what GoF had intended for it to do—illustrate how by loosening up classes, updating and adding new elements is easy. While I’ve been busy with OOPSLA and work, it took me a good deal of time to develop the original MVC. However, when it came to updating the program, I was able to do it quite quickly. If you’d like to download the entire set of files, click the download button that Barbara Parkman send us.

Make Your Own View
To really understand how the MVC works, you will find it helpful to create your own View for this program. Just add any kind of view you want, from simple to elaborate, and have it display data from the Model. There’s nothing you can break, and it will demonstrate how useful Design Patterns are when adding new elements to a program. In fact, we can make it into a Golden Lunch Bucket Contest if enough of you are interested. Enjoy!

The Truckin’ Through ActionScript 3.0 MVC: Part IV—Making Changes by William B. Sanders, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.
Related posts:

Bill Sanders
0 Response to “Truckin’ Through ActionScript 3.0 MVC: Part IV—Making Changes”