1.概述
在PureMVC实现的经典MVC元设计模式中,这三部分由三个单例模式类管理,分别是Model、View和Controller。三者合称为核心层或核心角色。各层之间能以一种松耦合的方式通信,并且与平台无关。
1.Model保存对Proxy对象的引用,Proxy负责操作数据模型;
2.View保存对Mediator对象的引用 。由Mediator对象来操作具体的视图组件,包括:添加事件监听器 ,发送或接收Notification ,直接改变视图组件的状态;
3.Controller保存所有Command的映射。Command类是无状态的,只在需要时才被创建。
4.Facade,单例模式,它负责初始化核心层(Model,View和Controller),还提供对已注册的类的管理。
使用pureMVC,我们只需要实现Proxy、Mediator和Command,还有一个Facade
2.实现Facade
package fad
{
import controller.*;
import org.puremvc.as3.interfaces.IFacade;
import org.puremvc.as3.patterns.facade.Facade;
/**
* 继承Facade
**/
public class ApplicationFacade extends Facade implements IFacade
{
//Notification name constants
public static const STARTUP:String ="startup";
public static const LOGIN:String ="login";
public static const CANCEL:String ="cancel";
public static const LOGINRESULT:String ="loginReslut";
public static const SHOW:String = "show";
public static const SHOWLOGIN:String = "showLogin";
/**
* 单例实现
**/
public static function getInstance():ApplicationFacade{
if(instance==null)
instance = new ApplicationFacade;
return instance as ApplicationFacade;
}
/**
* 自己初始化,注册了一些命令
**/
override protected function initializeController():void{
super.initializeController();
registerCommand(STARTUP,StartupCommand);
registerCommand(LOGIN,LoginCommand);
registerCommand(SHOW,ShowLoginCommand);
}
/**
* 启动函数,发送STARTUP通知,框架会新建StartupCommand命令,并执行
**/
public function startup(app:Object):void{
sendNotification(STARTUP,app);
}
}
}
3.command
Facade中注册了一些命令,需要实现
StartupCommand.as
package controller
{
import compents.m2login;
import model.LoginProxy;
import org.puremvc.as3.interfaces.ICommand;
import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.patterns.command.SimpleCommand;
import view.LoginMediator;
public class StartupCommand extends SimpleCommand implements ICommand
{
override public function execute(notif:INotification):void
{
// 初始化LoginPanel对应的Mediator
facade.registerProxy(new LoginProxy());
var app:Hello=notif.getBody()as Hello;//Hello是主界面的名字
facade.registerMediator(new LoginMediator(app));
}
}
}
LoginComand
package controller
{
import model.LoginProxy;
import model.vo.LoginUser;
import org.puremvc.as3.interfaces.ICommand;
import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.patterns.command.SimpleCommand;
import view.LoginMediator;
public class LoginCommand extends SimpleCommand implements ICommand
{
override public function execute(notif:INotification):void
{
var loginProxy:LoginProxy = facade.retrieveProxy(LoginProxy.NAME) as LoginProxy;
loginProxy.login(notif.getBody()as LoginUser);
}
}
}
StartupCommand继承了框架提供的SimpleCommand,程序启动时调用它注册 LoginProxy和LoginMediator。
Command对象是无状态的;
只有在需要的时候(Controller收到相应的Notification)才会被创建,并且在被执行(调用execute方法)之后就会被删除.
Command要实现ICommand接口。在PureMVC中有两个类实现了ICommand接口:SimpleCommand、MacroCommand。
SimpleCommand只有一个execute方法,execute方法接受一个Inotification实例做为参数。实际应用中,你只需要重写这个方法就行了。
MacroCommand在构造方法调用自身的initializeMacroCommand方法。实际应用中,你需重写这个方法,调用addSubCommand添加子Command。你可以任意组合SimpleCommand和MacroCommand成为一个新的Command。
4.Midiator
LoginMediator.as
package view
{
import fad.ApplicationFacade;
import flash.events.Event;
import model.LoginProxy;
import mx.controls.Alert;
import org.puremvc.as3.interfaces.IMediator;
import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.patterns.mediator.Mediator;
import view.components.LoginPanel;
public class LoginMediator extends Mediator implements IMediator {
public static const NAME:String = "LoginMediator";
public function LoginMediator(viewComponent:Object)
{
super(NAME,viewComponent);
//绑定界面事件处理函数
hello.addEventListener(LoginPanel.LOGIN,onLogin);
hello.addEventListener(LoginPanel.CANCEL,onCancel);
hello.addEventListener(ApplicationFacade.SHOW,onShow);
}
public function get hello():Hello{
return viewComponent as Hello;
}
private function onLogin(event:Event):void{
sendNotification(ApplicationFacade.LOGIN,hello.loginPanel.loginUser);
}
private function onCancel(event:Event):void{
sendNotification(ApplicationFacade.CANCEL);
}
private function onShow(event:Event):void{
this.sendNotification(ApplicationFacade.SHOW);
}
/**
* 以数据形式返回这些Notification 名称,表示这个miediator 响应这些名称的Notification,
*
**/
override public function listNotificationInterests():Array{
return[ApplicationFacade.LOGINRESULT,ApplicationFacade.CANCEL,ApplicationFacade.SHOWLOGIN];
}
/**
* Notification的响应函数
* */
override public function handleNotification(notfi:INotification):void{
switch(notfi.getName()){
case ApplicationFacade.LOGINRESULT:
var result:Boolean = new Boolean(notfi.getBody());
Alert.show(result?"登录成功":"登录失败");
break;
case ApplicationFacade.CANCEL:
Alert.show("取消登录");
this.hello.loginPanel.visible = false;
//facade.removeProxy(LoginProxy.NAME);
break;
case ApplicationFacade.SHOWLOGIN:
this.hello.loginPanel.visible = true;
break;
}
}
}
}
5.proxy
LoginProxy
package model
{
import model.vo.LoginUser;
import mx.controls.Alert;
import org.puremvc.as3.interfaces.IProxy;
import org.puremvc.as3.patterns.proxy.Proxy;
import fad.ApplicationFacade;
public class LoginProxy extends Proxy implements IProxy
{
public static const NAME:String="LOGIN_PROXY";
public function LoginProxy(){
super(NAME,new Object);
}
/**
* 公共方法,在Command中调用
*
**/
public function login(loginUser:LoginUser):void{
var result:Boolean = false;
if(loginUser.loginName=="user" && loginUser.loginPwd=="123456"){
result =true;
}
sendNotification(ApplicationFacade.LOGINRESULT,result);//通知mediator
}
/**
* 公共方法,在Command中调用
*
**/
public function show():void{
sendNotification(ApplicationFacade.SHOWLOGIN);//通知mediator
}}}
LoginUser 是一个自定义的数据类。
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的任何内容。
View 有个主界面Hello.mxml和一个LoginPanel.mxml
Hello.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns: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:compents="view.components.*"
creationComplete="appFacade.startup(this);"
click="onclick();" >
<fx:Metadata>
[Event('Login')]
[Event('Cancel')]
[Event('Show')]
</fx:Metadata>
<fx:Script>
<![CDATA[
import fad.ApplicationFacade;
private var appFacade:ApplicationFacade= ApplicationFacade.getInstance();
public function onclick():void{
dispatchEvent(new Event(ApplicationFacade.SHOW));
}
]]>
</fx:Script>
<fx:Declarations>
<!-- 将非可视元素(例如服务、值对象)放在此处 -->
</fx:Declarations>
<compents:LoginPanel x="595" y="116" id="loginPanel">
</compents:LoginPanel>
</s:Application>
LoginPanel.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:s="library://ns.adobe.com/flex/spark"
width="344" height="196" layout="absolute"
creationComplete="initApp()" title="Login">
<mx:Metadata>
[Event('Login')]
[Event('cancel')]
</mx:Metadata>
<mx:Script>
<![CDATA[
import model.vo.LoginUser;
[Bindable]
public var loginUser:LoginUser;
public static const LOGIN:String = "Login";
public static const CANCEL:String = "Cancel";
// send the named event
private function sendEvent(event:Event, eventName:String ):void
{
loginUser = new LoginUser(userName.text,pwd.text);
var hello:Hello = this.parent as Hello;
dispatchEvent( new Event( eventName, true ) );
event.stopPropagation();
}
private function initApp() : void {
userName.text="user";
pwd.text="123456";
}
]]>
</mx:Script>
<s:Label x="56" y="43" text="UserName"/>
<s:TextInput id="userName" x="120" y="41" />
<s:Label x="56" y="69" text="Passward"/>
<s:TextInput id ="pwd" x="120" y="67" displayAsPassword="true"/>
<s:Button x="100" y="115" name="login" label="Login" click="sendEvent(event,LOGIN)"/>
<s:Button x="187" y="115" label="Cancel" click="sendEvent(event,CANCEL)"/>
</mx:Panel>