在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
}
}
當然,源碼會奉上~