原文:http://www.as3dp.com/2007/12/27/minimalist-mvc-example-using-the-puremvc-framework/#more-52
纯属个人学习而翻译…..还没有完.
PureMVC框架初级入门
几个月前,我在寻找一种流线型的as程序开发框架,
Cairngorm 和PureMVC 是比较成熟的框架,PureMVC对我映像比较深刻,因为他有可靠的文档。PureMVC 带有一个概念图表,解释了框架使用的设计模式,那是我非常熟悉的。Cairngorm 没有类似的图表来解释它的结构。
这里的例子虽然不是一个完整的应用程序,但是,是最好的用来解释PureMVC框架的内部工作的例子。在开始之前,了解框架的一些重要介绍会有帮助的。
你触摸不到它们-模型,视图,控制器
虽然PureMVC是建立在传统的MVC模式,你不能建立或者直接操作这些实体(模型-视图-控制器)。它们都作为单独者了,本质上是隐藏的。你不用开发它们的类,由 proxy,mediator,command 类来担任程序中的通讯。概念图表中显示了这些。
“成双协作”用于孤军作战
Cliff 介绍了“成双协作”的概念。在传统的MVC模式中,Model 包含了数据和商业逻辑;View 表现为用户界面的组件;Controller 操作用户的输入和操作 model。无论如何,在PureMVC中,它们都不能直接存取,它们由其它的成双设计模式来提供更多的存取和loose-coupling 用于传统的设计模式。
Model-Proxy 成双协作:提供存取和操作程序的数据。你用model开发和注册一个proxy类来提供控制存取一个数据对像。例如:一个proxy可以从硬盘上加载数据文件,或者提供访问一个远程数据库或者web service。proxy模式提供了一个接口给程序,隐藏了详细的存取和操作数据。
View-Mediator 成双协作:展示用户界面元件和更新。你开发有形的Mediator 类注册到View 来提供与UI组件通讯的程序接口。一个单独的Mediator类可以有多个UI组件。例如:一个登录屏幕可以有一个mediator 类,可以包含用户名和密码框和一个发送按纽。Mediator模式可以隐藏复杂的UI元件之间的交互。
Command-Controller 成双协作:UI组件可以操作model。在传统的MVC程序中,Controller 在strategy 模式中担当一个strategy 。在这里,controller 特殊的执行决定程序怎么样对用户输入作出反应。例如,controller可以检查用户输入的有效性和决定如果程序数据需要被更新。在PureMVC,strategy 代码被嵌入Command对像中。这在本质上隐藏proxy类和proxy方法(呼叫操作mediator的数据对像),减轻mediator 和proxy之间的沟通。
在Events之前思考Notifications
mediators, proxies and commands三者之间交谈是用一个唯一消息经过方案叫:Notifications(通知)。通知本质上触发,它可以传送数据。Commands 发送明确的通知。只有mediators侦听通知。所以,一个proxy或者command可以发送通知,触发一个mediator去更新一个UI组件。Proxies可以发送,但是不会侦听通知。一个mediator可以操作程序数据,通过发送一个通知来触发一个command去更新数据对像通过proxy。
事件在flash和flex中都没有丢弃,mediators可以注册和侦听从UI组件来的事件,像按纽和输入框。proxies注册和侦听的事件:当处理数据对像,像当加载一个XML数据文件时。
Facade 是你的朋友
你必须建立一个有形的Facade 类,让整个框架更容易使用。Facade提供一个单独的类:统一PureMVC工作所需的所有方法。另外,实例化一个facade,建立一个model,view,controller,初始化框架
在开始建立一个PureMVC程序时,这四个重大的概念需要明白。
The Minimalist Example入门例子
这个例子使用Flash CS3开发,基本的侦听用户按键,储存最后按下的键和显示键。这个例子是用PureMVC 1.6写的,
确保你已下载了框架和它已出现在你的类路径中。这个例子的包层次展示如下:

下面这个图表展示了类和通知消息。开始程序时,呼叫 starup()方法,在Facade类实例ApplicationFacade中。数字显示动作发生的次序:
- ApplicationFacade 类广播了一个 STARTUP Notification;
- STARTUP Notification 触发了command对像:
StartupCommand(来注册KeyDataProxy proxy和 stagemediator mediator),
- StageMediator mediator 注册了一个KEY_DOWN 事件操作者到舞台,来截取按下的键。它也注册用来接受KEYUPDATE Notifications
- 用户按下一个键,产生一个KEY_DOWN Event
StageMediator broadcasts a KEYDOWN Notification with the character code of the key in the notification body.
- The KEYDOWN Notification triggers the
StoreKeyCommand command object, which accesses the KeyDataProxy proxy and updates the model (data object).
- The
KeyDataProxy proxy stores the data in a data object (just a property in the proxy in this case).
- The
KeyDataProxy proxy broadcasts a KEYUPDATE Notification with the character code of the key in the notification body.
- The KEYUPDATE Notification handler in
StageMediator intercepts the KEYUPDATE Notification and traces the key value.
The StageMediator mediator could have directly updated the model in step 5 through the KeyDataProxy proxy, without going through a command object. However, this introduces a tighter coupling between the Mediator and Proxy that should best be avoided.
The Concrete Facade
The first step is to create a concrete Facade class called ApplicationFacade to declare notification names and bind them to command objects.
The ApplicationFacade class defines the STARTUP, KEYDOWN and KEYUPDATE notification name constants. STARTUP and KEYDOWN are bound to the StartupCommand and StoreKeyCommand comand objects. In contrast, the KEYUPDATE notification is not bound to a command object. It is broadcast by the proxy to mediators informing them that the model has been updated.
The Facade is the entry point into the application and defines the startup() method that broadcasts a STARTUP Notification to the rest of the application. The startup() method has to be called from the document class of your Flash document (see Document Class below). Typically, the argument passed to the startup method should be the stage instance.
ApplicationFacade.as
package { import org.puremvc.interfaces.IFacade;
import org.puremvc.patterns.facade.Facade;
import org.puremvc.patterns.observer.Notification;
import controller.*;
public class ApplicationFacade extends Facade implements IFacade { // 通知名
public static const STARTUP:String = “startup”;
public static const KEYDOWN:String = “key down”;
public static const KEYUPDATE:String = “key update”;
// Singleton ApplicationFacade Factory Method 工厂方法
public static function getInstance(): ApplicationFacade {
if (instance == null) {
instance = new ApplicationFacade( );
}
return instance as ApplicationFacade;
}// 广播STARTUP Notification
public function startup(app:Object):void {
notifyObservers(new Notification(STARTUP, app));
} // Register Commands with the Controller
override protected function initializeController():void {
super.initializeController();
registerCommand(STARTUP, StartupCommand);
registerCommand(KEYDOWN, StoreKeyCommand);
}
}
}
The KEYDOWN notification is sent by the StageMediator Mediator (see below) when a user presses a key. The KEYUPDATE notification is sent by the KeyDataProxy Proxy (see below) to all mediators informing them that the model has changed.
KEYDOWN 通知是由StageMediator 发送的(当用户按下一个按键时)。KEYUPDATE 通知是由KeyDataProxy 发送给所有mediator,告诉他们models已经改变了。
The Commands
这里有两个command对像:
StartupCommand和StoreKeyCommand
StartupCommand.as
package controller {
import flash.display.Stage;
import org.puremvc.interfaces.ICommand;
import org.puremvc.interfaces.INotification;
import org.puremvc.patterns.command.SimpleCommand;
import view.*;
import model.*;
public class StartupCommand extends SimpleCommand
implements ICommand {
// Register the Proxies and Mediators
override public function execute(note:INotification):void{
// Create and register proxy
facade.registerProxy(new KeyDataProxy());
// Create and register StageMediator with the
stage instance as an
argument
var stage:Stage = note.getBody() as Stage;
facade.registerMediator(new StageMediator(stage));
}
}
}
When the StartupCommand command object is triggered by the STARTUP notification it first registers the proxy called KeyDataProxy. It then registers the StageMediator Mediator and passes the stage to it. This is an essential practice in Flash applications as the Mediators require access to the stage to add UI components to it. Note that the StartupCommand command object expects the STARTUP notification to contain a reference to the stage in the notification body.
StoreKeyCommand.as
package controller { import flash.display.Stage;
import org.puremvc.interfaces.ICommand;
import org.puremvc.interfaces.INotification;
import org.puremvc.patterns.command.SimpleCommand;
import model.*;
public class StoreKeyCommand extends SimpleCommand
implements ICommand {
override public function execute(note:INotification):void {// Get the KeyDataProxy proxy var keyDataProxy:KeyDataProxy =
facade.retrieveProxy(KeyDataProxy.NAME) as KeyDataProxy; // Set the char code of the key pressed in
KeyDataProxy var charCode:uint = note.getBody() as uint; keyDataProxy.key = charCode; } } }
The StoreKeyCommand comand object is triggered by the KEYDOWN notification that is sent by the StageMediator Mediator (see below). The KEYDOWN notification contains the character code of the pressed key in the message body. The command code retrieves the KeyDataProxy using the retrieveProxy() method and stores the character code using a setter method.
The Mediator
The example application just has one Mediator class called StageMediator that mediates interactions with the stage. It registers a Key Down even handler called onKeyDownEvent() with the stage. It also registers to receive KEYUPDATE notifications by overriding the listNotificationInterests() method. It also overrides the handleNotification() method to intercept and act on KEYUPDATE notifications.
StageMediator.as
package view { import flash.events.KeyboardEvent;
import flash.display.Stage;
import org.puremvc.interfaces.IMediator;
import org.puremvc.interfaces.INotification;
import org.puremvc.patterns.mediator.Mediator;
// Mediator for interacting with the Stage.
public class StageMediator extends Mediator
implements IMediator {
// Cannonical name of the Mediator
public static const NAME:String = ‘StageMediator’;
// Constructor
public function StageMediator(viewComponent:Stage) {
// pass the viewComponent to the superclass
super(viewComponent);
// Listen for events from the view component
viewComponent.addEventListener(
KeyboardEvent.KEY_DOWN, onKeyDownEvent);
} // Key down event handler
private function onKeyDownEvent(event:KeyboardEvent):void {
sendNotification(ApplicationFacade.KEYDOWN, event.charCode);
} // Return list of Nofitication names that Mediator is
interested in
override public function listNotificationInterests():Array {
return [ApplicationFacade.KEYUPDATE];
} // Handle all notifications this Mediator is interested in
override public function handleNotification(
note:INotification):void {
switch (note.getName()) {
case ApplicationFacade.KEYUPDATE:
var charCode:uint = note.getBody() as uint;
trace(charCode);
break;
}
} // Get the Mediator name
override public function getMediatorName():String {
return NAME;
}
}
}
The StageMediator mediator broadcasts a KEYDOWN Notification when it receives a KEY_DOWN even from the Flash event model. The character code of the key pressed is sent in the body of the Notification. On receipt of a KEYUPDATE Notification, the StageMediator simply traces it to the output panel (to keep things simple). It could have updated a UI element (e.g. Text Field) to show a change.
The Proxy
The minimalist example has one proxy called KeyDataProxy keeps track of the last key pressed by the user. The character code of the key is stored in the lastKeyPressed property in the proxy itself. The proxy could just have easily stored this value in an external data file or remote database if needed.
KeyDataProxy.as
package model { import org.puremvc.interfaces.IProxy;
import org.puremvc.patterns.proxy.Proxy;
public class KeyDataProxy extends Proxy implements IProxy {
// Cannonical name of the Proxy
public static const NAME:String = ‘KeyDataProxy’;
private var lastKeyPressed:uint = undefined; // Constructor
public function KeyDataProxy() {
super(NAME);
// pass the cannonical name to the superclass
} public function set key(charCode:uint):void {
if (lastKeyPressed != charCode) {
lastKeyPressed = charCode;
// Create and send a KEYUPDATE Notification and pass the
charCode as an argument
sendNotification(ApplicationFacade.KEYUPDATE,
charCode);
}
}
}
}
Note that the model stores the new character code only if it is different from the one already stored. It also doesn’t broadcast a KEYUPDATE Notification unless this is the case. This is why we needed two Notification messages, one to broadcast that a key has been pressed (KEYDOWN) and another to inform all interested mediators that the model has been updated with a new character code (KEYUPDATE).
The Document Class
The document class of the Flash document initializes the concrete facade and broadcasts the STARTUP notification.
Main.as
package { import flash.display.MovieClip; /**
* Main Class
* @ purpose: Document class for movie
*/
public class Main extends MovieClip {
public function Main() {
// Instantiate the ApplicationFacade
var facade:ApplicationFacade =
ApplicationFacade.getInstance();
// Call the application startup method and pass the
stage as the argument
facade.startup(this.stage);
}
}
}
So, there you have it, a minimalist example in PureMVC implemented in the Flash CS3 environment. Make sure you Disable keyboard shortcuts from the Control menu when testing the application. The source can be downloaded below.
MVC_minimalist_v1.zip.
I’ll follow this up with another post extending the minimalist application a little bit (e.g. adding more UI elements and mediators) to demonstrate the value of the design decisions that went into PureMVC. This post is quite long and I’m sure there are some errors – comments and corrections are welcome.