今天学习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);
}
}
}