今天學習PureMVC,看了教程,寫了Demo,記錄并分享學習過程。
教程參考:http://puremvc.org/component/option,com_wrapper/Itemid,183/
PureMVC使用觀察者模式,将代碼分離為三個離散的層:模式、視圖和控制器,這三部分由三個單例模式類管理,分别是Model、View和Controller,三者合稱為核心層或核心角色。PureMVC中還有另外一個單例模式類——Façade,Façade提供了與核心層通信的唯一接口,以簡化開發複雜度。
PureMVC的通信不采用Flash的EventDispatcher/Event,是因為PureMVC可能運作在沒有FlashEvent和EventDispatcher類的環境中。
PureMVC架構示意圖:
由圖很明顯可以知道PureMVC的層次結構:
- Model層:由Value Object和Proxy組成。Proxy負責操作資料模型,與遠端服務通信存取資料。Proxy發送Notification,但不接收Notification,比如Proxy從遠端服務接收到資料或資料被更新時,都要發送Notification告訴系統,由View層和Controller層來接收并做相應回報到界面。
- View層:由UI和對應的Mediator組成。Mediator儲存一個或多個View Component的引用,通過View Component自身提供的API管理它們。Mediator既能發送Notification也能接受Notification,主要職責是處理View Component派發的事件和系統其它部分發出來的Notification(通知)。
- Controller層:由Command和Facade組成。Command實作應用程式的業務邏輯。可以擷取Proxy對象并與之互動,發送Notification,執行其它的Command。用于複雜的或系統範圍的操作,如應用程式的“啟動”和“關閉”。Command接收通知也可以發出通知。實際Command類可以繼承自SimpleCommand(對于單獨業務),也可以繼承自MacroCommand(如果需要執行多個業務邏輯)。Facade類應用單例模式,負責初始化核心層(Model,View和Controller),并能通路它們的public方法。在實際的應用中,隻需要繼承Facade類建立一個具體的Facade類(一般命名為ApplicationFacade)就可以實作整個的MVC模式,并不需要在代碼中導入編寫Model,View和Controller的類。
Facade和Proxy隻能發送Notification,Mediator既可以發送也可以接收Notification,Notification被映射到Command,同時Command也可以發送Notification,這是一種“釋出/訂閱”機制,所有的觀察者都可以收到相同的通知。例如多個書刊訂閱者可以訂閱同一份雜志,當雜志有新刊物出版時,所有的訂閱者都會被通知。
下面開始寫例子PureMVCDemo:完成使用者登入在背景進行驗證合法性,并回報給前台。
版本說明:
Flex:Flex4
ActionScript:ActionScript 3.0
Java JDK:jdk1.6.0_20
PureMVC:PureMVC_AS3_2_0_4,官方網站提供多種語言的資源包下載下傳,在這裡用ActionScript版本,下載下傳連結http://trac.puremvc.org/PureMVC_AS3/。将下載下傳到的資源檔案PureMVC_AS3_2_0_4.swc拷貝到Flex工程的libs目錄下即可。
工具及環境和Java背景服務應用的建立過程介紹:詳見我的上篇文章,BlazeDS實作Flex和Java通信的Demo
1、背景Java服務端項目工程PureMVCDemo組成:
相應代碼:
UserVO.java
package net.dreamhui.java;
public class UserVO {
publicString userName;
publicString passWord;
publicUserVO()
{
//和ActionScript對應得構造方法
}
//getters& setters
publicString getUserName() {
returnuserName;
}
publicvoid setUserName(String userName) {
this.userName= userName;
}
publicString getPassWord() {
returnpassWord;
}
publicvoid setPassWord(String passWord) {
this.passWord= passWord;
}
}
LoginUser.java
package net.dreamhui.java;
public class LoginUser {
publicUserVO currentUser;
privateString uName;
privateString pWord;
//Flex端要調用的服務
publicUserVO login(UserVO par_user)
{
//UserVOpar_user2 = UserVO(par_user);
uName= par_user.userName;
pWord= par_user.passWord;
if(uName.equalsIgnoreCase("wwh")&&pWord.equalsIgnoreCase("wwh"))
{
returnpar_user;
//return"歡迎使用者:"+uName;
}
else{
returnnull;
//return"使用者名或密碼錯誤,請重新輸入";
}
}
}
配置檔案remoting-config.xml要添加的内容:
<destinationid="loginUser">
<properties>
<source>net.dreamhui.java.LoginUser</source>
</properties>
</destination>
2,前台Flex工程PureMVCDemo工程組成:
相應代碼(按照開發流程):
LoginPanel.mxml
<?xml version="1.0"encoding="utf-8"?>
<s:Panelxmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
title="請登陸"currentState="initState"
creationComplete="lvcreationComplete(event)">
<!--~~~~~~~~~~~~~~~~~~~~~~Script~~~~~~~~~~~~~~~~~~~~~~-->
<fx:Script>
<![CDATA[
importmx.controls.Alert;
importmx.core.UIComponent;
importmx.events.FlexEvent;
importmx.events.ValidationResultEvent;
importmx.rpc.events.FaultEvent;
importmx.rpc.events.ResultEvent;
importmx.validators.Validator;
importnet.dreamhui.controller.events.LoginEvent;
importnet.dreamhui.model.vo.UserVO;
privatevar validObjs:Array;
[Bindable]
privatevar _currUser:UserVO;// = new UserVO();
protectedfunction lvcreationComplete(event:FlexEvent):void
{
validObjs= [unSV,pwSV];
}
protectedfunction submit(event:MouseEvent):void
{
varvalidatorResults:Array;
validatorResults= Validator.validateAll(validObjs);
if(validatorResults.length== 0)
{
varuser:UserVO = new UserVO();
user.userName= uName.text;
user.passWord= pWord.text;
var lgEvent:LoginEvent= new LoginEvent(LoginEvent.LOGIN_USER);
lgEvent.data= user;
dispatchEvent(lgEvent);
//Alert.show("dispatchEvent");
//派發事件
}
else
{
//定義校驗出錯事件
varvEvent:ValidationResultEvent;
//取出第一個出錯事件
vEvent= validatorResults[0] as ValidationResultEvent;
//将光标定位到第一個出錯的元件上
(vEvent.target.sourceas UIComponent).setFocus();
}
}
[Bindable]
publicfunction get currUser():UserVO
{
return_currUser;
}
publicfunction set currUser(value:UserVO):void
{
_currUser= value;
}
]]>
</fx:Script>
<!--~~~~~~~~~~~~~~~~~~~~~~states~~~~~~~~~~~~~~~~~~~~~~-->
<s:states>
<s:Statename="initState"/>
<s:Statename="loginState"/>
</s:states>
<!--~~~~~~~~~~~~~~~~~~~~~~Declarations~~~~~~~~~~~~~~~~~~~~~~-->
<fx:Declarations>
<!--定義使用者名和密碼的輸入校驗類-->
<mx:StringValidatorid="unSV"
source="{uName}"
property="text"
required="true"
maxLength="10"
tooLongError="使用者名最長為10位"
requiredFieldError="請填寫使用者名"/>
<mx:StringValidatorid="pwSV"
source="{pWord}"
property="text"
required="true"
maxLength="10"
tooLongError="密碼最長為10位"
requiredFieldError="請填寫密碼"/>
</fx:Declarations>
<!--~~~~~~~~~~~~~~~~~~~~~~UIComponents~~~~~~~~~~~~~~~~~~~~~~-->
<mx:FormincludeIn="initState">
<mx:FormItemlabel="使用者名" >
<s:TextInputid="uName" />
</mx:FormItem>
<mx:FormItemlabel="密碼" >
<s:TextInputid="pWord" displayAsPassword="true" />
</mx:FormItem>
<mx:FormItem>
<s:Buttonid="submitBtn" click="submit(event)" label="登陸"right="0" />
</mx:FormItem>
</mx:Form>
<s:HGroupincludeIn="loginState" top="20" left="10" >
<s:Label text="歡迎尊貴的使用者:"/>
<s:Label id="cuName" text="{currUser.userName}"/>
</s:HGroup>
</s:Panel>
UserVO.as
package net.dreamhui.model.vo
{
[Bindable]
[RemoteClass(alias="net.dreamhui.java.UserVO")]
publicclass UserVO
{
privatevar _userName:String;
privatevar _passWord:String;
publicfunction UserVO()
{
}
//getters& setters
publicfunction get userName():String
{
return_userName;
}
publicfunction set userName(value:String):void
{
_userName= value;
}
publicfunction get passWord():String
{
return_passWord;
}
publicfunction set passWord(value:String):void
{
_passWord= value;
}
}
}
LoginEvent.as
package net.dreamhui.controller.events
{
importflash.events.Event;
publicclass LoginEvent extends Event
{
privatevar _data:Object;
publicstatic const LOGIN_USER:String = "loginUser";
publicfunction LoginEvent(type:String, bubbles:Boolean=false,cancelable:Boolean=false)
{
super(type,bubbles, cancelable);
}
publicfunction get data():Object
{
return_data;
}
publicfunction set data(value:Object):void
{
_data= value;
}
}
}
LoginPanelMediator.as
package net.dreamhui.view
{
importnet.dreamhui.controller.ApplicationFacade;
importnet.dreamhui.controller.events.LoginEvent;
importnet.dreamhui.model.LoginProxy;
importnet.dreamhui.model.vo.UserVO;
importnet.dreamhui.view.ui.LoginPanel;
importmx.controls.Alert;
importorg.puremvc.as3.interfaces.IMediator;
importorg.puremvc.as3.interfaces.INotification;
importorg.puremvc.as3.patterns.mediator.Mediator;
publicclass LoginPanelMediator extends Mediator implements IMediator
{
publicstatic const NAME:String = "LoginPanelMediator";
publicfunction LoginPanelMediator(viewComponent:LoginPanel)
{
super(NAME,viewComponent);
viewComponent.addEventListener(LoginEvent.LOGIN_USER,login);
//添加視圖事件監聽,當點選“登入”按鈕時觸發
}
publicfunction login(event:LoginEvent):void
{
//Alert.show("LoginPanelMediatorlogin");
varlgUser:UserVO = event.data as UserVO;
sendNotification(ApplicationFacade.USER_LOGIN,lgUser);
}
overridepublic function listNotificationInterests():Array
{
return[LoginProxy.LOGIN_YES,LoginProxy.LOGIN_NO];
}
overridepublic function handleNotification(notification:INotification):void
{
switch(notification.getName())
{
caseLoginProxy.LOGIN_YES:
//通知來源LoginProxy,如果使用者名和密碼正确
loginPanel.currUser= notification.getBody() as UserVO;
loginPanel.currentState= "loginState";
break;
caseLoginProxy.LOGIN_NO:
//通知來源LoginProxy,如果使用者名或密碼錯誤或遠端服務調用失敗
//Alert.show("使用者名或密碼錯誤,請重新填寫");
Alert.show(notification.getBody().toString());
}
}
protectedfunction get loginPanel():LoginPanel
{
returnviewComponent as LoginPanel;
}
}
}
LoginCommand.as
package net.dreamhui.controller.business
{
importnet.dreamhui.model.LoginProxy;
importnet.dreamhui.model.vo.UserVO;
importmx.controls.Alert;
importorg.puremvc.as3.interfaces.INotification;
importorg.puremvc.as3.patterns.command.SimpleCommand;
publicclass LoginCommand extends SimpleCommand
{
overridepublic function execute(notification:INotification):void
{
//Alert.show("LoginCommandexecute");
varlgUser:UserVO = notification.getBody() as UserVO;
//擷取通知攜帶參數,類型為Object,轉換為需要的類型
varlgProxy:LoginProxy = facade.retrieveProxy(LoginProxy.NAME) as LoginProxy;
//檢索到負責遠端過程調用的業務代理LoginProxy
lgProxy.userLogin(lgUser);
//調用遠端過程,對調用過程的結果處理,在LoginProxy裡面
}
}
}
LoginProxy.as
package net.dreamhui.model
{
importmx.rpc.events.FaultEvent;
importmx.rpc.events.ResultEvent;
importmx.rpc.remoting.RemoteObject;
importmx.controls.Alert;
importnet.dreamhui.model.vo.UserVO;
importorg.puremvc.as3.interfaces.IProxy;
importorg.puremvc.as3.patterns.proxy.Proxy;
publicclass LoginProxy extends Proxy implements IProxy
{
publicstatic const NAME:String = "LoginProxy";
publicstatic const LOGIN_YES:String = "loginYes";
publicstatic const LOGIN_NO:String = "loginNo";
//聲明常量,避免手誤導緻編譯運作錯誤
privatevar loginService:RemoteObject;
publicfunction LoginProxy()
{
super(NAME,new UserVO());
loginService= new RemoteObject();
loginService.destination= "loginUser";
//初始化遠端過程調用的RemoteObject執行個體
loginService.addEventListener(FaultEvent.FAULT,onFault);
loginService.login.addEventListener(ResultEvent.RESULT,onResult);
//給遠端過程調用添加事件監聽,在監聽函數裡對調用傳回結果做處理
}
publicfunction userLogin(par_U:UserVO):void
{
//Alert.show("LoginProxyuserLogin");
loginService.login(par_U);
//調用服務端的業務處理方法
}
protectedfunction onFault(event:FaultEvent):void
{
//Alert.show("onFault");
sendNotification(LOGIN_NO,event.fault.faultDetail);
}
protectedfunction onResult(event:ResultEvent):void
{
//Alert.show("onResult");
if(event.resultas UserVO == null)
{
//傳回null,調用成功,但拒絕登陸成功,發出通知LOGIN_NO
sendNotification(LOGIN_NO,"使用者名或密碼錯誤,請重新輸入");
}
else
{
//傳回正常UserVO,調用成功,登陸成功,發出通知LOGIN_YES
sendNotification(LOGIN_YES,event.resultas UserVO);
}
}
}
}
ApplicationFacade.as
package net.dreamhui.controller
{
importorg.puremvc.as3.interfaces.IFacade;
importorg.puremvc.as3.patterns.facade.Facade;
importnet.dreamhui.controller.business.StartupCommand;
importnet.dreamhui.controller.business.LoginCommand;
publicclass ApplicationFacade extends Facade implements IFacade
{
publicstatic const STARTUP:String = "startUp";
publicstatic const USER_LOGIN:String = "userLogin";
publicstatic function getInstance():ApplicationFacade
{
if(instance== null)
{
instance= new ApplicationFacade();
}
returninstance as ApplicationFacade;
}
overrideprotected function initializeController():void
{
super.initializeController();
registerCommand(STARTUP,StartupCommand);
registerCommand(USER_LOGIN,LoginCommand);
}
publicfunction startup(app:PureMVCDemo):void
{
sendNotification(STARTUP,app);
}
}
}
StartupCommand.as
package net.dreamhui.controller.business
{
importorg.puremvc.as3.patterns.command.MacroCommand;
publicclass StartupCommand extends MacroCommand
{
overrideprotected function initializeMacroCommand():void
{
addSubCommand(ModelPreCommand);
addSubCommand(ViewPreCommand);
}
}
}
ModelPreCommand.as
package net.dreamhui.controller.business
{
importnet.dreamhui.model.LoginProxy;
importorg.puremvc.as3.interfaces.INotification;
importorg.puremvc.as3.patterns.command.SimpleCommand;
importorg.puremvc.as3.patterns.observer.*;
publicclass ModelPreCommand extends SimpleCommand
{
overridepublic function execute(notification:INotification):void
{
facade.registerProxy(newLoginProxy());
//注冊應用程式所需要的Proxy
}
}
}
ViewPreCommand.as
package net.dreamhui.controller.business
{
importnet.dreamhui.view.ApplicationMediator;
importnet.dreamhui.view.LoginPanelMediator;
importnet.dreamhui.view.ui.LoginPanel;
importorg.puremvc.as3.interfaces.INotification;
importorg.puremvc.as3.patterns.command.SimpleCommand;
publicclass ViewPreCommand extends SimpleCommand
{
overridepublic function execute(notification:INotification):void
{
//注冊應用程式的View視圖和對應的Mediator
varapp:PureMVCDemo = notification.getBody() as PureMVCDemo;
facade.registerMediator(newApplicationMediator(app));
varlgP:LoginPanel = app.getElementAt(0) as LoginPanel;
facade.registerMediator(newLoginPanelMediator(lgP));
}
}
}
PureMVCDemo.mxml
<?xml version="1.0"encoding="utf-8"?>
<s:Applicationxmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955"minHeight="600"
xmlns:ui="net.dreamhui.view.ui.*"
creationComplete="{facade.startup(this);}">
<!--~~~~~~~~~~~~~~~~~~~~~~~~Script~~~~~~~~~~~~~~~~~~~~~~~~-->
<fx:Script>
<![CDATA[
importmx.controls.Alert;
importnet.dreamhui.controller.ApplicationFacade;
//聲明并執行個體化控制器ApplicationFacade
privatevar facade:ApplicationFacade = ApplicationFacade.getInstance();
]]>
</fx:Script>
<!--~~~~~~~~~~~~~~~~~~~~~~~~UIComponents~~~~~~~~~~~~~~~~~~~~~~~~-->
<ui:LoginPanelid="lgPanel" width="340" height="200"top="20"
horizontalCenter="-30" fontSize="20" />
</s:Application>
ApplicationMediator.as
package net.dreamhui.view
{
importorg.puremvc.as3.interfaces.IMediator;
importorg.puremvc.as3.patterns.mediator.Mediator;
publicclass ApplicationMediator extends Mediator implements IMediator
{
publicstatic const NAME:String = "ApplicationMediator"
publicfunction ApplicationMediator(viewComponent:PureMVCDemo)
{
super(NAME,viewComponent);
}
}
}