原文:http://www.as3dp.com/2007/12/27/ ... -framework/#more-52
纯属个人学习而翻译…..还没有完.
PureMVC框架初级入门
几个月前,我在寻找一种流线型的as程序开发框架,
Cairngorm 和PureMVC 是比较成熟的框架,PureMVC对我映像比较深刻,因为他有可靠的文档。PureMVC 带有一个概念图表,解释了框架使用的设计模式,那是我非常熟悉的。Cairngorm 没有类似的图表来解释它的结构。
Cairngorm or PureMVC?
Recently, Ali Mills and Luke Bayes made a presentation on Flex Application Frameworks. Their choices essentially boiled down to Cairngorm and PureMVC and concluded that PureMVC came out on top. However, I highly recommend that developers watch the whole presentation as many enterprise developers in the audience made comments that challenged the easy differentiation of the two frameworks. Having the backing of a stable organization like Adobe, as is the case with Cairngorm, means a lot for enterprise development. Also, this post on Bill Lane’s blog and several comments implying that PureMVC is much harder to learn than Cairngorm got me thinking if a really simple application would help scaffold the transition to PureMVC.
I’ve implemented the minimalist example from the MVC chapter of our book (available as a free download from Adobe Devnet) using the PureMVC framework. Now this is not a full-fledged application, but a very minimal example meant to explain the inner workings of the PureMVC framework. Before diving in, a brief introduction to some of the important aspects of the framework will help. This was the hard stuff that I had to internalize before starting out. These concepts are more eloquently explained in much greater detail by Cliff Hall in the framework documentation.
这里的例子虽然不是一个完整的应用程序,但是是最好的用来解释PureMVC框架的内部工作的例子。在开始之前,了解框架的一些重要介绍会有帮助的。
“You can’t touch this” - models, views and controllers that is你触摸不到它们
Although PureMVC is built on the traditional Model-View-Controller meta-pattern, you cannot create or directly manipulate these entities. The Model, View and Controller are already implemented in PureMVC as singletons and are essentially hidden. You do not develop Model, View and Controller classes but implement proxy, mediator, and command classes that broker the communication in your application. The PureMVC Conceptual Diagram shows this visually.
虽然PureMVC是建立在传统的MVC模式,你不能建立或者直接操作这些实体(模型-视图-控制器)。它们都作为单独者了,本质上是隐藏的。你不用开发它们的类,由 proxy,mediator,command 类来担任程序中的通讯。概念图表中显示了这些。
“Collaboration pairs” are better than singles
“成双协作”用于孤军作战
Cliff introduces the concept of “collaboration pairs”. In the traditional MVC pattern, the Model contains the data and business logic; the View presents the user interface components; and the Controller handles the user input and manipulates the model. However, since the model, view and controller classes are not directly accessible in PureMVC, they form collaboration pairs with other design patterns to provide more abstraction and loose-coupling than the traditional MVC pattern.
Cliff 介绍了“成双协作”的概念。在传统的MVC模式中,Model 包含了数据和商业逻辑;View 表现为用户界面的组件;Controller 操作用户的输入和操作 model。无论如何,在PureMVC中,它们都不能直接存取,它们由其它的成双设计模式来提供更多的存取和loose-coupling 用于传统的设计模式。
The Model - Proxy collaboration pair provides a means to access and manipulate application data. You develop and register a concrete proxy class with the model to provide controlled access to a data object. For example, a proxy can load data files from disk, or provide access to a remote database or web service. The proxy pattern provides an interface to the core application that hides the implementation details of accessing and manipulating data and application state.
Model-Proxy 成双协作:提供存取和操作程序的数据。你用model开发和注册一个proxy类来提供控制存取一个数据对像。例如:一个proxy可以从硬盘上加载数据文件,或者提供访问一个远程数据库或者web service。proxy模式提供了一个接口给程序,隐藏了详细的存取和操作数据。
The View - Mediator collaboration pair provides the means by which the user interface elements are presented and updated. You develop concrete Mediator classes that register with the View to provide the interface by which the core application communicates with the UI components. A single mediator class can have multiple UI components. For example. a login screen will have one mediator class but can contain username and password fields, and a submit button. The mediator pattern can hide the complex interactions between related UI elements from the core application.
View-Mediator 成双协作:展示用户界面元件和更新。你开发有形的Mediator 类注册到View 来提供与UI组件通讯的程序接口。一个单独的Mediator类可以有多个UI组件。例如:一个登录屏幕可以有一个mediator 类,可以包含用户名和密码框和一个发送按纽。Mediator模式可以隐藏复杂的UI元件之间的交互。
The Command - Controller collaboration pair represents the means by which UI elements can manipulate the model. In a traditional MVC app, the Controller acts as a concrete strategy in a strategy pattern. In this sense, controllers are specific implementations that determine how the application responds to user input. For example, controllers can do validation checks on user input and decide if application data needs to be updated (through a proxy). In PureMVC, the strategy code is encapsulated inside Command Objects. This essentially hides the proxy class and proxy methods that are called to manipulate data objects from the mediator, reducing the coupling between the mediator and the proxy.
Command-Controller 成双协作:UI组件可以操作model。在传统的MVC程序中,Controller 担当一个strategy 在strategy 模式中。在这里,controller 特殊的执行决定程序怎么样对用户输入作出反应。例如,controller可以检查用户输入的有效性和决定如果程序数据需要被更新。在 PureMVC,strategy 代码被嵌入Command对像中。这在本质上隐藏proxy类和proxy方法(呼叫操作mediator的数据对像),减轻mediator 和proxy之间的沟通。
Again. the PureMVC Conceptual Diagram does a really nice job of showing the “collaboration pairs.”
Think in Notifications before Events事件在通知之前
The mediators, proxies and commands talk to each other using a unique message passing scheme called Notifications. Notifications are essentially triggers that can carry data. Commands are bound to specific notifications. Only mediators listen for notifications. So, a proxy or command can send a notification that will trigger a Mediator to update a UI component. Proxies can send, but do not listen to notifications. A mediator can manipulate application data by sending a notification that will trigger a command to update a data object through a proxy. This shrewd design decision to develop a custom notification scheme has allowd Cliff to port the PureMVC framework to AS2, C#, Java, PHP, and ColdFusion.
mediators, proxies and commands三者之间交谈是用一个唯一消息经过方案叫:通知。通知本质上触发,它可以传送数据。Commands 发送明确的通知。只有mediators侦听通知。所以,一个proxy或者command可以发送通知,触发一个mediator去更新一个UI组件。 Proxies可以发送,但是不会侦听通知。一个mediator可以操作程序数据,通过发送一个通知来触发一个command去更新数据对像通过 proxy。
The Events in Flash and Flex are not discarded, but used at the boundary of the application. Mediators can register and listen for events from UI components such as buttons and fields. Proxies register and listen for events when dealing with data objects such as when loading an XML data file asynchronously.
事件在flash和flex中都没有丢弃,mediators可以注册和侦听从UI组件来的事件,像按纽和输入框。proxies注册和侦听的事件:当处理数据对像,像当加载一个XML数据文件时。
The Facade is your friend Facade是你的朋友
You must create a concrete Facade class that makes the whole framework easier to use. The Facade provides a single class that unifies all the methods required to make PureMVC work. In addition, instantiating a facade initializes the PureMVC framework by creating the model, view and controller singletons.
你必须建立一个有形的Facade 类,当整个框架更容易使用。Facade提供一个单独的类:统一PureMVC工作所需的所有方法。另外,实例化一个facade,建立一个model,view,controller,初始化框架
These were the four big concepts that I needed to understand before diving into creating a PureMVC application.
在开始建立一个PureMVC程序时,这四个重大的概念需要明白。
The Minimalist Example入门例子
The example was developed using Flash CS3 and basically listens for user key presses, stores the last key pressed, and displays the key (MVC_minimalist_v1.zip). The example app was written in PureMVC Release 1.6. The package structure will change in release 2.0 and the import statements will need to be changed accordingly.
这个例子使用Flash CS3开发,基本的侦听用户按键,储存最后按下的键和显示键。这个例子是用PureMVC 1.6写的,
Make sure to download and install the PureMVC framework and make it accessible in your classpath first. The package hierarchy as shown in the Flash Project Panel is shown here for reference.
确保你已下载了框架和它已出现在你的类路径中。这个例子的包层次展示如下:
Here is a conceptual diagram that shows the classes and the notification messages. To start the application, call the startup() method in the the Concrete Facade class called ApplicationFacade. The numbers show the sequence of actions that take place.
下面这个图表展示了类和通知消息。开始程序时,呼叫 starup()方法,在Facade类实例ApplicationFacade中。数字显示动作发生的次序:
The Concrete Facade class called ApplicationFacade broadcasts a STARTUP Notification. Facade类的实例广播了一个 STARTUP 通知;
The STARTUP Notification triggers the StartupCommand command object which registers the KeyDataProxy proxy, and StageMediator mediator. STARTUP 通知触发了command对像:StartupCommand(注册为KeyDataProxy proxy和 stagemediator的 mediator),
StageMediator mediator registers a KEY_DOWN Event handler to the stage to intercept key presses. It also registers to receive KEYUPDATE Notifications.
User presses a key generating a KEY_DOWN Event that is intercepted by the key down event handler in the StageMediator.
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 注册Proxies and Mediators override as3的覆盖语法关键字.
ide public function execute(note:INotification):void{
// Create and register proxy 创建和注册一个proxy
facade.registerProxy(new KeyDataProxy());
// Create and register StageMediator with the stage instance as an argument 以后stage为构造参数创建一个StageMediator 并且将它注册
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.
当StartupCommand 命令对象被STARTUP notification触发时,它首先注册一个KeyDataProxy类型的proxy,之后注册一个StageMediator类型的 Mediator以及传递stage参数给它.在flash应用程序中,这是一个必要的实现当Mediators要求有权使用stage将UI组建添加到 StartupCommand中. 注意 StartupCommand 命令对象期望在STARTUP notification对象中存在一个stage类型的引用.
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 设置在KeyDataProxy中键盘按下的字符码,uint是无符号32位数,此处进行强制类型转换
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.
StoreKeyCommand命令对象被StageMediator Mediator 发送的KEYDOWN notification触发.在KEYDOWN notification的消息体中包含有按下键的字符码.命令程序获得KeyDataProxy对象可以使用retrieveProxy()方法而存储 字符码用一个set方法.
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的Mediator类,它和stage相互协调影响.在stage内,它将一个Key Down事件注册给onKeyDownEvent().同时还获得KEYUPDATE notifications通过覆盖listNotificationInterests()方法,它还覆盖了handleNotification() 方法拦截以及调用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.
StageMediator协调者广播一个 KEYDOWN 的通知,当它在flash的事件模型中获得一个KEY_DOWN事件.在Notification的内部,按下键对应的字符码会被发送.在的到一个 KEYUPDATE Notification的收条后,StageMediator能够轻松的将它输出到面板(让事物简单).它可以更新一个UI元素(例如 Text Filed)去显示一个改变.
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的代理保存最后一个被用户按下的键的状态.这个键的字符码被存储在代理自身的lastKeyPressed属性中.代理可以很轻松的将这个值可以根据需要存放到外部的数据文件中或者远程数据库.
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).
注意model存储新的字符码只有在与之前存储的制字符码不一致的情况下.同样它也不会广播一个KEYUPDATE通知.这就是为什么我们需要2个通知消 息,一个用来广播键已经被按下,另一个用来通知所有相关的在model已经被用一个新的字符码更新的mediators.
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.
这样看来,你已经了解它了,一个在flash CS3环境下用PureMVC实现迷你邮件列表例子.确定你已经让你键盘的快捷键失效通过控制菜单当你测试这个例子的时候.源程序可以在下面下载.
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.
革命尚未成功 同志仍需努力,继这篇文章之后我会发表它的姐妹篇,将现有系统稍微扩展一下(例如 添加更多的UI组件和协调者)来演示PureMVC的设计理念.这篇文章有点长,我相信这里存在一些错误,欢迎批评指正.