在PureMVC实现的经典MVC元设计模式中,这三部分由三个单例模式类管理,分别是Model、View和Controller。三者合称为核心层或核心角色。各层之间能以一种松耦合的方式通信,并且与平台无关。PureMVC的目标之一就是保持平台无关性,不管你使用什么技术什么UI组件什么数据结构都能应用PureMVC框架。它已经被移植到其他的一些平台像C#、Silverlight和J2ME.
在我以前的文中已经说过mvc是三层结构,puremvc同样由三层构成:
1.Model保存对Proxy对象的引用,Proxy负责操作数据模型;
2.View保存对Mediator对象的引用 。由Mediator对象来操作具体的视图组件,包括:添加事件监听器 ,发送或接收Notification ,直接改变视图组件的状态;
3.Controller保存所有Command的映射。Command类是无状态的,只在需要时才被创建。
以下是个小例子,功能是点击tab按钮时,切换界面。
首先是文档类Main.as:
package
{
import flash.display.Sprite;
import flash.events.Event;
import KingClass.ApplicationFacade
/**
* ...
* @author Never
*/
public class Main extends Sprite
{
//通过单例模式 获取ApplicationFacade
private var facade:ApplicationFacade = ApplicationFacade.getInstance();
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
//启动PureMVC,在应用程序中调用此方法,并传递应用程序本身的引用
facade.startup(this);
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
}
}
}
{
import flash.display.Sprite;
import flash.events.Event;
import KingClass.ApplicationFacade
/**
* ...
* @author Never
*/
public class Main extends Sprite
{
//通过单例模式 获取ApplicationFacade
private var facade:ApplicationFacade = ApplicationFacade.getInstance();
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
//启动PureMVC,在应用程序中调用此方法,并传递应用程序本身的引用
facade.startup(this);
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
}
}
}
在文档类中我们调用了一个ApplicationFacade的单例模式类。ApplicationFacade继承
Façade,
Façade
类应用单例模式,它负责初始化核心层(Model,View和Controller),并能访问它们的Public方法.这样,在实际的应用中,你只需继承Façade类创建一个具体的Façade类就可以实现整个MVC模式,并不需要在代码中导入编写Model,View和Controller类。ApplicationFacade有个startup方法,负责启动PureMVC,我们在文档类中调用此方法,并传递应用程序本身的引用,有一个可选的“报体”,“报体”可以是任意ActionScript对象。在这里是main。
ApplicationFacade.as:
package KingClass{
import KingClass.Controller.StartupCommand;
import org.puremvc.as3.patterns.facade.Facade;
public class ApplicationFacade extends Facade {
public static const STARTUP:String = "startup";
public static const CHANGETABLE:String = "changeTable";
//得到ApplicationFacade单例的工厂方法
public static function getInstance():ApplicationFacade {
if (instance == null)
instance = new ApplicationFacade();
return instance as ApplicationFacade;
}
/*启动PureMVC,在应用程序中调用此方法,并传递应用程序本身的引用
有一个可选的“报体”,“报体”可以是任意ActionScript对象。*/
public function startup(app:Object):void {
sendNotification(STARTUP, app);
}
//注册Command,建立Command与Notification之间的映射
override protected function initializeController():void {
super.initializeController();
registerCommand(STARTUP, StartupCommand);
}
}
}
import KingClass.Controller.StartupCommand;
import org.puremvc.as3.patterns.facade.Facade;
public class ApplicationFacade extends Facade {
public static const STARTUP:String = "startup";
public static const CHANGETABLE:String = "changeTable";
//得到ApplicationFacade单例的工厂方法
public static function getInstance():ApplicationFacade {
if (instance == null)
instance = new ApplicationFacade();
return instance as ApplicationFacade;
}
/*启动PureMVC,在应用程序中调用此方法,并传递应用程序本身的引用
有一个可选的“报体”,“报体”可以是任意ActionScript对象。*/
public function startup(app:Object):void {
sendNotification(STARTUP, app);
}
//注册Command,建立Command与Notification之间的映射
override protected function initializeController():void {
super.initializeController();
registerCommand(STARTUP, StartupCommand);
}
}
}
好了,下面我们来看看三层结构如何规划,我们根据文档类的顺序,首先介绍
Command,
Command
对象是无状态的;只有在需要的时候(Controller收到相应的Notification)才会被创建,并且在被执行(调用execute方法)之后就会被删除.Command要实现ICommand接口。在PureMVC中有两个类实现了ICommand接口:SimpleCommand、MacroCommand。SimpleCommand只有一个execute方法,execute方法接受一个Inotification实例做为参数。实际应用中,你只需要重写这个方法就行了。MacroCommand在构造方法调用自身的initializeMacroCommand方法。实际应用中,你需重写这个方法,调用addSubCommand添加子Command。你可以任意组合SimpleCommand和MacroCommand成为一个新的Command。
StartupCommand.as 它继承
MacroCommand,启动两个继承
SimpleCommand的command。
package KingClass.Controller
{
import KingClass.ApplicationFacade;
import org.puremvc.as3.patterns.command.MacroCommand;
public class StartupCommand extends MacroCommand
{
override protected function initializeMacroCommand():void{
this.addSubCommand(ModelPrepCommand);
this.addSubCommand(ViewPrepCommand);
/* 注册添加、删除用户命令 */
facade.registerCommand(ApplicationFacade.CHANGETABLE,ChangeComman);
}
}
}
{
import KingClass.ApplicationFacade;
import org.puremvc.as3.patterns.command.MacroCommand;
public class StartupCommand extends MacroCommand
{
override protected function initializeMacroCommand():void{
this.addSubCommand(ModelPrepCommand);
this.addSubCommand(ViewPrepCommand);
/* 注册添加、删除用户命令 */
facade.registerCommand(ApplicationFacade.CHANGETABLE,ChangeComman);
}
}
}
ModelPrepCommand.as 它注册一个model
package KingClass.Controller
{
import org.puremvc.as3.patterns.command.SimpleCommand;
import org.puremvc.as3.interfaces.INotification;
import KingClass.Model.ShowProxy;
/**
* ...
* @author Never
*/
public class ModelPrepCommand extends SimpleCommand
{
public function ModelPrepCommand() {
return;
}
override public function execute(notification:INotification):void
{
/* 注册Model */
facade.registerProxy(new ShowProxy());
}
}
}
{
import org.puremvc.as3.patterns.command.SimpleCommand;
import org.puremvc.as3.interfaces.INotification;
import KingClass.Model.ShowProxy;
/**
* ...
* @author Never
*/
public class ModelPrepCommand extends SimpleCommand
{
public function ModelPrepCommand() {
return;
}
override public function execute(notification:INotification):void
{
/* 注册Model */
facade.registerProxy(new ShowProxy());
}
}
}
ViewPrepCommand.as(初始化界面)
package KingClass.Controller
{
import org.puremvc.as3.patterns.command.SimpleCommand;
import org.puremvc.as3.interfaces.ICommand;
import org.puremvc.as3.interfaces.INotification;
import KingClass.View.*;
/**
* ...
* @author Never
*/
public class ViewPrepCommand extends SimpleCommand
{
public function ViewPrepCommand() {
return;
}
override public function execute(notification:INotification):void
{
var obj:Main;
obj = notification.getBody() as Main;
facade.registerMediator(new ShowMediator(obj));
facade.registerMediator(new TopMediator(obj));
return;
}
}
}
{
import org.puremvc.as3.patterns.command.SimpleCommand;
import org.puremvc.as3.interfaces.ICommand;
import org.puremvc.as3.interfaces.INotification;
import KingClass.View.*;
/**
* ...
* @author Never
*/
public class ViewPrepCommand extends SimpleCommand
{
public function ViewPrepCommand() {
return;
}
override public function execute(notification:INotification):void
{
var obj:Main;
obj = notification.getBody() as Main;
facade.registerMediator(new ShowMediator(obj));
facade.registerMediator(new TopMediator(obj));
return;
}
}
}
Mediator
Mediator保存了一个或多个View Component的引用,通过View Component自身提供的API管理它们.Mediator的主要职责是处理View Component派发的事件和系统其他部分发出来的Notification(通知)。因为Mediator也会经常和Proxy交互,所以经常在Mediator的构造方法中取得Proxy实例的引用并保存在Mediator的属性中,这样避免频繁的获取Proxy实例。
通常一个Mediator只对应一个View Component,但却可能需要管理多个UI控件
1.检查事件类型或事件的自定义内容。
2.检查或修改View Component的属性(或调用提供的方法)。
3.检查或修改Proxy对象公布的属性(或调用提供的方法)。
4.发送一个或多个Notification,通知别的Mediatora或Command作出响应(甚至有可能发送给自身)。
在 Mediator实例化时,PureMVC会调用Mediator的listNotificationInterests方法查询其关心的 Notification,Mediator则在listNotificationInterests方法中以数据形式返回这些Notification 名称,例如下面的ShowMediator.as。Mediator对外不应该公布操作View Component的函数。而是自己接收Notification做出响应来实现。
ShowMediator.as(为可以切换的界面ShowCom.as的引用,他是根据传递的报体main添加组件到显示列表中)
package KingClass.View
{
import flash.display.DisplayObject;
import org.puremvc.as3.patterns.mediator.Mediator;
import org.puremvc.as3.interfaces.IMediator;
import org.puremvc.as3.interfaces.ICommand;
import org.puremvc.as3.interfaces.INotification;
import KingClass.Model.ShowProxy;
import KingClass.View.Components.ShowCom;
import KingClass.ApplicationFacade
import KingClass.Model.Vo.IndexVo;
/**
* ...
* @author Never
*/
public class ShowMediator extends Mediator implements IMediator
{
public static const NAME:String = "ShowMediator";
private var _showCom:ShowCom = new ShowCom();
public function ShowMediator(viewComponent:DisplayObject = null)
{
super(NAME, viewComponent);
main.addChild(_showCom);
}
private function get main():Main {
return viewComponent as Main;
}
override public function listNotificationInterests():Array {
return [
ApplicationFacade.CHANGETABLE
];
}
override public function handleNotification(notification:INotification):void {
switch(notification.getName()) {
case ApplicationFacade.CHANGETABLE:
_showCom.Goto(int((facade.retrieveProxy(ShowProxy.NAME) as ShowProxy).newData.index));
break;
}
}
//end class
}
}
{
import flash.display.DisplayObject;
import org.puremvc.as3.patterns.mediator.Mediator;
import org.puremvc.as3.interfaces.IMediator;
import org.puremvc.as3.interfaces.ICommand;
import org.puremvc.as3.interfaces.INotification;
import KingClass.Model.ShowProxy;
import KingClass.View.Components.ShowCom;
import KingClass.ApplicationFacade
import KingClass.Model.Vo.IndexVo;
/**
* ...
* @author Never
*/
public class ShowMediator extends Mediator implements IMediator
{
public static const NAME:String = "ShowMediator";
private var _showCom:ShowCom = new ShowCom();
public function ShowMediator(viewComponent:DisplayObject = null)
{
super(NAME, viewComponent);
main.addChild(_showCom);
}
private function get main():Main {
return viewComponent as Main;
}
override public function listNotificationInterests():Array {
return [
ApplicationFacade.CHANGETABLE
];
}
override public function handleNotification(notification:INotification):void {
switch(notification.getName()) {
case ApplicationFacade.CHANGETABLE:
_showCom.Goto(int((facade.retrieveProxy(ShowProxy.NAME) as ShowProxy).newData.index));
break;
}
}
//end class
}
}
TopMediator.as(为可以按钮界面TopCom.as的引用,而组件showCom发生变化时,它监听自定义的时间UIEvent)
package KingClass.View
{
import flash.display.DisplayObject;
import KingClass.Model.Vo.IndexVo;
import org.puremvc.as3.patterns.mediator.Mediator;
import org.puremvc.as3.interfaces.IMediator;
import KingClass.Event.UIEvent
import KingClass.View.*;
import KingClass.Model.*;
import KingClass.View.Components.TopCom
/**
* ...
* @author Never
*/
public class TopMediator extends Mediator implements IMediator
{
public static const NAME:String = "TopMediator";
/*因为Mediator也会经常和Proxy交互,所以经常在Mediator的构造方法中取得Proxy实例的引用并保存在Mediator的属性中,这样避免频繁的获取Proxy实例*/
private var _showProxy:ShowProxy = new ShowProxy();
public function TopMediator(viewComponent:DisplayObject = null)
{
super(NAME, viewComponent);
viewComponent.addEventListener(TopCom.EVENT_BTN_CLICK, onEventClick );
var _TopCom:TopCom = new TopCom();
_TopCom.y = 180;
main.addChild(_TopCom);
}
private function get main():Main {
return viewComponent as Main;
}
private function onEventClick(e:UIEvent = null):void {
_showProxy.newData=new IndexVo(e.data as int)
}
//end class
}
}
{
import flash.display.DisplayObject;
import KingClass.Model.Vo.IndexVo;
import org.puremvc.as3.patterns.mediator.Mediator;
import org.puremvc.as3.interfaces.IMediator;
import KingClass.Event.UIEvent
import KingClass.View.*;
import KingClass.Model.*;
import KingClass.View.Components.TopCom
/**
* ...
* @author Never
*/
public class TopMediator extends Mediator implements IMediator
{
public static const NAME:String = "TopMediator";
/*因为Mediator也会经常和Proxy交互,所以经常在Mediator的构造方法中取得Proxy实例的引用并保存在Mediator的属性中,这样避免频繁的获取Proxy实例*/
private var _showProxy:ShowProxy = new ShowProxy();
public function TopMediator(viewComponent:DisplayObject = null)
{
super(NAME, viewComponent);
viewComponent.addEventListener(TopCom.EVENT_BTN_CLICK, onEventClick );
var _TopCom:TopCom = new TopCom();
_TopCom.y = 180;
main.addChild(_TopCom);
}
private function get main():Main {
return viewComponent as Main;
}
private function onEventClick(e:UIEvent = null):void {
_showProxy.newData=new IndexVo(e.data as int)
}
//end class
}
}
最后是
Proxy
Proxy
是有状态的,当状态发生变化时发Mediator,将数据的变化反映到视图。Proxy可能会提供访问Data Object部分属性或方法的API,也可能直接提供Data Object的引用。如果提供了更新Data Object的方法,那么在数据被修改时可能会发送一个Notifidation通知系统的其它部分。Proxy不监听Notification,也永远不会被通知,因为Proxy并不关心View的状态。但是,Proxy提供方法和属性让其它角色更新数据。View本质上是显示Model的数据并让用户能与之交互,我们期望一种单向依赖,即View依赖于Model,而Model却不依赖于View。View必须知道Model的数据是什么,但Model却并不需要知道View的任何内容。
我们在这个例子中定义了一个ShowProxy.as的类,它的底层数据来自于IndexVo.as
ShowProxy.as(当数据层发生改变时 它发出一个notification,通知view做出新的变化,比如切换界面)
package KingClass.Model
{
import KingClass.Model.Vo.IndexVo;
import org.puremvc.as3.patterns.observer.Notification;
import org.puremvc.as3.patterns.proxy.Proxy;
import org.puremvc.as3.interfaces.IProxy;
import KingClass.ApplicationFacade;
/**
* ...
* @author Never
*/
public class ShowProxy extends Proxy implements IProxy
{
public static const NAME:String = "ShowProxy";
private var newdata:Object;
public function ShowProxy(str:String=""):void
{
super(NAME, str);
}
//Proxy是有状态的,当数据发生改变时 我们发送一个Notification。
public function set newData(n:Object):void{
newdata = n;
sendNotification(ApplicationFacade.CHANGETABLE,this)
}
/* 添加项 */
public function get newData():Object{
return this.newdata;
}
}
}
{
import KingClass.Model.Vo.IndexVo;
import org.puremvc.as3.patterns.observer.Notification;
import org.puremvc.as3.patterns.proxy.Proxy;
import org.puremvc.as3.interfaces.IProxy;
import KingClass.ApplicationFacade;
/**
* ...
* @author Never
*/
public class ShowProxy extends Proxy implements IProxy
{
public static const NAME:String = "ShowProxy";
private var newdata:Object;
public function ShowProxy(str:String=""):void
{
super(NAME, str);
}
//Proxy是有状态的,当数据发生改变时 我们发送一个Notification。
public function set newData(n:Object):void{
newdata = n;
sendNotification(ApplicationFacade.CHANGETABLE,this)
}
/* 添加项 */
public function get newData():Object{
return this.newdata;
}
}
}
IndexVo.as(这是proxy操控的数据层)
package KingClass.Model.Vo
{
/**
* ...
* @author Never
*/
public class IndexVo extends Object
{
private var _index:int;
public function IndexVo(n:int)
{
this.index=n
}
public function set index(n:int):void {
this._index = n;
}
public function get index():int{
return this._index;
}
}
}
{
/**
* ...
* @author Never
*/
public class IndexVo extends Object
{
private var _index:int;
public function IndexVo(n:int)
{
this.index=n
}
public function set index(n:int):void {
this._index = n;
}
public function get index():int{
return this._index;
}
}
}
另外还需要一个ChangeComman.as,他是注册一个新的proxy。即我们需要的ChangeComman.这在proxy数据发生变化时,已经发出notification了。
package KingClass.Controller
{
import org.puremvc.as3.patterns.command.SimpleCommand;
import org.puremvc.as3.interfaces.ICommand;
import org.puremvc.as3.interfaces.INotification;
import KingClass.View.Components.TopCom;
import KingClass.Model.ShowProxy;
/**
* ...
* @author Never
*/
public class ChangeComman extends SimpleCommand
{
public function ChangeComman()
{
}
override public function execute(notification:INotification):void
{
/* 注册Model */
var showProxy:ShowProxy = notification.getBody() as ShowProxy;
facade.registerProxy(showProxy);
}
//end class
}
}
{
import org.puremvc.as3.patterns.command.SimpleCommand;
import org.puremvc.as3.interfaces.ICommand;
import org.puremvc.as3.interfaces.INotification;
import KingClass.View.Components.TopCom;
import KingClass.Model.ShowProxy;
/**
* ...
* @author Never
*/
public class ChangeComman extends SimpleCommand
{
public function ChangeComman()
{
}
override public function execute(notification:INotification):void
{
/* 注册Model */
var showProxy:ShowProxy = notification.getBody() as ShowProxy;
facade.registerProxy(showProxy);
}
//end class
}
}
当然,源码会奉上~
接下来是: