您的位置:首页 > 运维架构 > 网站架构

Flex 开发架构渐变

2009-03-04 17:52 288 查看
Flex 无疑是RIA第一位得选择,而JAVA 可以是Enterprise application 的第一选择。 结合二者来开发Web App 无疑是一种走向流行的方案。 前端Flex+后端JAVA的简单架构如图:



在实际得开发中实现上述结构的方法非常之多。我只是将自己开发的渐变过程记录下来。希望对后来者有所借鉴。

这个系列blog包括:

一。混沌未开-Flex-all-in-one

二。中央管理-Flex Central Managerment

三。MVC框架-Flex Cairngorm

四。咔嚓Front Controller的Cairngorm

五。轮回转世-Mate

样例。

为简洁起见,用一个非常简单的样例来演示开发过程:密友列表

系统只有二个画面:

登录-LoginView:



输入用户名和密码并登录, 进入密友列表画面(BuddyListView):



由于这个系列blog焦点在Flex应用开发方式,所以只选择Remote Object 作为和后端通讯的方式。AMF的实现使用Adobe 的opensource data service-BlazeDS。Java代码非常简单。仅限于配合这个blog系列。

相关得配置文件和JAVA class 如下:

1。remot-config.xml:

Xml代码

<?xml version=”1.0″ encoding=”UTF-8″?>

<service id=”remoting-service”

class=”flex.messaging.services.RemotingService”>

<adapters>

<adapter-definition id=”java-object” class=”flex.messaging.services.remoting.adapters.JavaAdapter” default=”true”/>

</adapters>

<default-channels>

<channel ref=”my-amf”/>

</default-channels>

<destination id=”flexmvcRO”>

<properties>

<source>com.ny.blog.flex.mvc.accessor.DummyAccessor</source>

<scope>session</scope>

</properties>

<adapter ref=”java-object” />

</destination>

</service>

<?xml version=”1.0″ encoding=”UTF-8″?>
<service id=”remoting-service”
class=”flex.messaging.services.RemotingService”>

<adapters>
<adapter-definition id=”java-object” class=”flex.messaging.services.remoting.adapters.JavaAdapter” default=”true”/>
</adapters>

<default-channels>
<channel ref=”my-amf”/>
</default-channels>
<destination id=”flexmvcRO”>
<properties>
<source>com.ny.blog.flex.mvc.accessor.DummyAccessor</source>
<scope>session</scope>
</properties>
<adapter ref=”java-object” />
</destination>

</service>


2.DummyAccessor.java:

Java代码

package com.ny.blog.flex.mvc.accessor;

import java.util.ArrayList;

import java.util.List;

import com.ny.blog.flex.mvc.pojo.Friend;

public class DummyAccessor {

public DummyAccessor() {

}

public boolean login(String userName,String password){

return true;

}

public List<Friend> getAllFriends(String userName){

List<Friend> myBuddy = new ArrayList<Friend>();

Friend dummy1 = new Friend();

dummy1.setFirstName(”John”);

dummy1.setLastName(”Smith”);

myBuddy.add(dummy1);

Friend dummy2 = new Friend();

dummy2.setFirstName(”Andy”);

dummy2.setLastName(”Jones”);

myBuddy.add(dummy2);

Friend dummy3 = new Friend();

dummy3.setFirstName(”Michael”);

dummy3.setLastName(”Niu”);

myBuddy.add(dummy3);

return myBuddy;

}

package com.ny.blog.flex.mvc.accessor;

import java.util.ArrayList;
import java.util.List;

import com.ny.blog.flex.mvc.pojo.Friend;

public class DummyAccessor {

public DummyAccessor() {
}

public boolean login(String userName,String password){
return true;
}

public  List<Friend> getAllFriends(String userName){
List<Friend> myBuddy = new ArrayList<Friend>();
Friend dummy1 = new Friend();
dummy1.setFirstName(”John”);
dummy1.setLastName(”Smith”);
myBuddy.add(dummy1);

Friend dummy2 = new Friend();
dummy2.setFirstName(”Andy”);
dummy2.setLastName(”Jones”);
myBuddy.add(dummy2);

Friend dummy3 = new Friend();
dummy3.setFirstName(”Michael”);
dummy3.setLastName(”Niu”);
myBuddy.add(dummy3);
return myBuddy;
}


3.相关 pojo Friend.java:

Java代码

public class Friend {

private String firstName;

private String lastName;

private String nickName;

public Friend() {

}

//getter and setters



}

public class Friend {
private String firstName;
private String lastName;
private String nickName;

public Friend() {
}

//getter and setters
…
}


Flex 开发架构(一): 混沌未开-Flex-all-in-one

混沌未开,顾名思义就是匹萨店里面烘烤的色香味俱全的一个pizza大饼,在一个面饼的底板上,混合所有的原料。 Lets make Pizza!

Flex与生俱来的是它的事件驱动(event-driven)的特点。就是说,Flex可以使用它的标签做到任何事情。因此开发一个Flex应用程序最简单,最基础的方法就是使用Flex标签,先来看看代码。

Loginview,用户界面部分:

Xml代码

<mx:Form id=”loginForm” x=”0″ y=”0″>

<mx:FormItem label=”Username:” >

<mx:TextInput id=”username” />

</mx:FormItem>

<mx:FormItem label=”Password:” >

<mx:TextInput id=”password” displayAsPassword=”true” />

</mx:FormItem>

<mx:FormItem direction=”horizontal” verticalGap=”15″ paddingTop=”5″ width=”170″>

<mx:Button id=”loginBtn” label=”Login” click=”login()”/>

</mx:FormItem>

</mx:Form>

<mx:Form id=”loginForm” x=”0″ y=”0″>
<mx:FormItem label=”Username:” >
<mx:TextInput id=”username” />
</mx:FormItem>
<mx:FormItem label=”Password:” >
<mx:TextInput id=”password” displayAsPassword=”true” />
</mx:FormItem>
<mx:FormItem direction=”horizontal” verticalGap=”15″ paddingTop=”5″ width=”170″>
<mx:Button id=”loginBtn” label=”Login” click=”login()”/>
</mx:FormItem>
</mx:Form>


使用 <mx:RemoteObject> 标签调用远程服务:

Xml代码

<mx:RemoteObject id=”loginReq” destination=”flexmvcRO”>

<mx:method name=”login” result=”loginHandler(event)” fault=”mx.controls.Alert.show(event.fault.faultString)”>

<mx:arguments>

<userName>{username.text}</userName>

<password>{password.text}</password>

</mx:arguments>

</mx:method>

</mx:RemoteObject>

<mx:RemoteObject  id=”loginReq” destination=”flexmvcRO”>
<mx:method name=”login” result=”loginHandler(event)” fault=”mx.controls.Alert.show(event.fault.faultString)”>
<mx:arguments>
<userName>{username.text}</userName>
<password>{password.text}</password>
</mx:arguments>
</mx:method>
</mx:RemoteObject>


现在,在login方法中发送请求:

Xml代码

if(Validator.validateAll(validators).length == 0){

loginReq.login.send();

}

if(Validator.validateAll(validators).length == 0){
loginReq.login.send();
}


在发送请求之后,需要建立一个返回结果的处理方法:

Xml代码

private function loginHandler(event:ResultEvent):void{

var isLogin:Boolean = event.result as Boolean;

if(isLogin){

this.parentApplication.viewStack.selectedIndex=1;

dispatchEvent(new LoginUserEvent(username.text));

}

}

private function  loginHandler(event:ResultEvent):void{
var isLogin:Boolean = event.result as Boolean;
if(isLogin){
this.parentApplication.viewStack.selectedIndex=1;
dispatchEvent(new LoginUserEvent(username.text));
}
}


最后,在页面之间建立联系,在这里我使用播送事件:

Xml代码

dispatchEvent(new LoginUserEvent(username.text));

dispatchEvent(new LoginUserEvent(username.text));


播送用户自定义的事件,就必须在代码最前面写入下述的元标签代码:

Xml代码

<mx:Metadata>

[Event(name="loginUser", type="flash.events.Event")]

</mx:Metadata

<mx:Metadata>
[Event(name="loginUser", type="flash.events.Event")]
</mx:Metadata


然后,建立用户自定义的事件LoginUserEvent.as:

Xml代码

import flash.events.Event;

public class LoginUserEvent extends Event

{

public static const LOGINUSEREVENT:String =”loginUser”;

public var loginUserName:String = “”;

public function LoginUserEvent(userName:String)

{

super(LOGINUSEREVENT, true, true);

this.loginUserName = userName;

}

override public function clone():Event {

return new LoginUserEvent(loginUserName);

}

import flash.events.Event;
public class LoginUserEvent extends Event
{
public static const LOGINUSEREVENT:String =”loginUser”;
public var loginUserName:String = “”;

public function LoginUserEvent(userName:String)
{
super(LOGINUSEREVENT, true, true);
this.loginUserName = userName;
}
override public function clone():Event {

return new LoginUserEvent(loginUserName);

}


当登录成功后,密友列表页面将会显示,BuddyListVew.mxml:

重要的是在列表代码中,首先要监听LoginUserEvent,因此要创建一个preinitialize的方法:

Xml代码

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” title=”Buddy List of {loginUserName}” preinitialize=”init()” width=”500″ height=”320″>

private function init():void{

this.parentApplication.addEventListener(LoginUserEvent.LOGINUSEREVENT, getUserName);

}

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” title=”Buddy List of {loginUserName}”     preinitialize=”init()” width=”500″ height=”320″>
private function init():void{
this.parentApplication.addEventListener(LoginUserEvent.LOGINUSEREVENT, getUserName);
}


然后完成事件监听的管理程序:

Xml代码

private function getUserName(event:LoginUserEvent):void{

loginUserName = event.loginUserName;

loginReq.getAllFriends.send();

}

private function getUserName(event:LoginUserEvent):void{
loginUserName = event.loginUserName;
loginReq.getAllFriends.send();
}


发送获得全部密友列表的请求,并且创建对应的管理方法:

Xml代码

private function getAllFriendsHandler(event:ResultEvent):void{

mybuddyList = event.result as ArrayCollection;

}

private function getAllFriendsHandler(event:ResultEvent):void{
mybuddyList = event.result as ArrayCollection;

}


在代码中,必须包含定义远程对象的标签:

Xml代码

<mx:RemoteObject id=”loginReq” destination=”flexmvcRO”>

<mx:method name=”getAllFriends” result=”getAllFriendsHandler(event)” fault=”mx.controls.Alert.show(event.fault.faultString)”>

<mx:arguments>

<userName>{loginUserName}</userName>

</mx:arguments>

</mx:method>

</mx:RemoteObject>

<mx:RemoteObject  id=”loginReq” destination=”flexmvcRO”>
<mx:method name=”getAllFriends” result=”getAllFriendsHandler(event)” fault=”mx.controls.Alert.show(event.fault.faultString)”>
<mx:arguments>
<userName>{loginUserName}</userName>
</mx:arguments>
</mx:method>
</mx:RemoteObject>


使用Flex标签完成程序方法非常简单,并且对于简单的系统来说, 也是非常有效,其商务逻辑层也不复杂。但在实际的应用中,并非只存在这样的项目,相反,实际工作中往往需要大量的远程对象的通讯。

好了,来看看:中央管理-Flex Central Management.

Flex Chaos-All-in-one这一节中所提到的,在大型项目中,将所有的代码放在一起并非明智之举,确切的讲:正确的方法是将商业逻辑层与UI层分离开来。

中央管理的理念是使用一个远程对象管理器来控制Flex与后端的通讯。其构建体系如下图所示



图中每一个UI组件都将调用一个服务(Service),服务类将调用中央管理器(Central Manager),中央管理器类将调用服务器端的解决方案。而图中全局对象管理器(Global Object Manager)将用来在UI之间传递数据。

现在来看看简单密友列表应用的实现。

首先是LoginView.xml

Xml代码

<?xml version=”1.0″ encoding=”utf-8″?>

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” width=”300″ height=”200″ horizontalAlign=”center” verticalAlign=”middle” title=”Flex Central Manager Login”>

<mx:Script>

<![CDATA[

import com.ny.flex.centralManagement.service.LoginService;

import mx.validators.Validator;

import mx.containers.ViewStack;

import mx.rpc.events.ResultEvent;

private function login():void{

if(Validator.validateAll(validators).length == 0){

LoginService.getInstance().login(username.text,password.text);

}

}

]]>

</mx:Script>

<!– Validators–>

<mx:Array id=”validators”>

<mx:StringValidator id=”userNameValidator” source=”{username}” property=”text” required=”true”/>

<mx:StringValidator id=”passwordValidator” source=”{password}” property=”text” required=”true” />

</mx:Array>

<mx:Form id=”loginForm” x=”0″ y=”0″>

<mx:FormItem label=”Username:” >

<mx:TextInput id=”username” />

</mx:FormItem>

<mx:FormItem label=”Password:” >

<mx:TextInput id=”password” displayAsPassword=”true” />

</mx:FormItem>

<mx:FormItem direction=”horizontal” verticalGap=”15″ paddingTop=”5″ width=”170″>

<mx:Button id=”loginBtn” label=”Login” click=”login()”/>

</mx:FormItem>

</mx:Form>

</mx:Panel>

<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” width=”300″ height=”200″ horizontalAlign=”center” verticalAlign=”middle” title=”Flex Central Manager Login”>
<mx:Script>
<![CDATA[
import com.ny.flex.centralManagement.service.LoginService;
import mx.validators.Validator;
import mx.containers.ViewStack;
import mx.rpc.events.ResultEvent;
private function login():void{
if(Validator.validateAll(validators).length == 0){
LoginService.getInstance().login(username.text,password.text);
}
}
]]>
</mx:Script>
<!– Validators–>
<mx:Array id=”validators”>
<mx:StringValidator id=”userNameValidator” source=”{username}” property=”text” required=”true”/>
<mx:StringValidator id=”passwordValidator” source=”{password}” property=”text” required=”true” />
</mx:Array>
<mx:Form id=”loginForm” x=”0″ y=”0″> <mx:FormItem label=”Username:” > <mx:TextInput id=”username” /> </mx:FormItem> <mx:FormItem label=”Password:” > <mx:TextInput id=”password” displayAsPassword=”true” /> </mx:FormItem> <mx:FormItem direction=”horizontal” verticalGap=”15″ paddingTop=”5″ width=”170″> <mx:Button id=”loginBtn” label=”Login” click=”login()”/> </mx:FormItem> </mx:Form>
</mx:Panel>


其功能的核心是:

LoginService.getInstance().login(username.text,password.text);

它的作用是有效的分离了商务逻辑层和视图组件。在此服务类程序不需支持任何状态,因此我们保持其单件模式(singleton)。

LoginService类文件如下:

Xml代码

import com.ny.flex.centralManagement.event.DataManagerResultEvent;

import com.ny.flex.centralManagement.manager.GlobalObjectManager;

import com.ny.flex.centralManagement.manager.RemoteObjectManager;

public class LoginService

{

public var roManager:RemoteObjectManager = null;

public var gom:GlobalObjectManager = GlobalObjectManager.getInstance();

private static var _instance:LoginService =null;

public static function getInstance():LoginService{

if(_instance == null){

_instance = new LoginService(new PrivateClass)

}

return _instance;

}

public function LoginService(privateclass:PrivateClass)

{

if(LoginService._instance == null){

LoginService._instance = this;

}

}

public function login(userName:String,password:String):void{

roManager = RemoteObjectManager.getRemoteObjectManager(”flexmvcRO”);

roManager.addEventListener(”getLoginUser”,loginHandler);

var params:Array = new Array(userName,password);

roManager.makeRemoteCall(”getLoginUserName”,”getLoginUser”,params);

}

private function loginHandler(event:DataManagerResultEvent):void {

var userName:String = event.result as String;

if(userName){

gom.loginUserName = userName;

gom.viewStackSelectedIndex=1;

}

}

}

import com.ny.flex.centralManagement.event.DataManagerResultEvent;
import com.ny.flex.centralManagement.manager.GlobalObjectManager;
import com.ny.flex.centralManagement.manager.RemoteObjectManager;

public class LoginService
{
public var roManager:RemoteObjectManager = null;
public var gom:GlobalObjectManager = GlobalObjectManager.getInstance();
private static var _instance:LoginService =null;

public static function getInstance():LoginService{
if(_instance == null){
_instance =  new LoginService(new PrivateClass)
}
return _instance;

}
public function LoginService(privateclass:PrivateClass)
{
if(LoginService._instance == null){
LoginService._instance =  this;
}
}
public function login(userName:String,password:String):void{
roManager = RemoteObjectManager.getRemoteObjectManager(”flexmvcRO”);
roManager.addEventListener(”getLoginUser”,loginHandler);
var params:Array = new Array(userName,password);

roManager.makeRemoteCall(”getLoginUserName”,”getLoginUser”,params);
}
private function loginHandler(event:DataManagerResultEvent):void {
var userName:String = event.result as String;
if(userName){
gom.loginUserName = userName;
gom.viewStackSelectedIndex=1;
}
}
}


代码中有两个特别的对象:

RemoteObjectManager

GlobalObjectManager

RemoteObjectManager是一个单件的类,用来实现中央管理所有的远程对象通讯。原始的代码来自于Jeff Tapper的博客:

Creating a Remote Object DataManager in ActionScript 3.0 for Flex 2.0

RemoteObjectManager.as:

Xml代码

package com.ny.flex.centralManagement.manager

{

import com.ny.flex.centralManagement.event.DataManagerResultEvent;

import flash.events.EventDispatcher;

import mx.core.Application;

import mx.resources.ResourceManager;

import mx.rpc.AbstractOperation;

import mx.rpc.AsyncToken;

import mx.rpc.events.FaultEvent;

import mx.rpc.events.ResultEvent;

import mx.rpc.remoting.mxml.RemoteObject;

public class RemoteObjectManager extends EventDispatcher {

public var ro:RemoteObject;

private var eventName:String;

private static var instanceMap:Object = new Object();

public function RemoteObjectManager(pri:PrivateClass,dest:String){

this.ro = new RemoteObject();

ro.destination = dest;

}

public static function getRemoteObjectManager(dest:String):RemoteObjectManager{

if(RemoteObjectManager.instanceMap[dest] == null){

RemoteObjectManager.instanceMap[dest] = new RemoteObjectManager(new PrivateClass(),dest);

}

var dm:RemoteObjectManager= RemoteObjectManager.instanceMap[dest];

return dm;

}

public function makeRemoteCall(methodName:String,eventName:String,args:Array=null):void{

this.eventName = eventName;

var op:mx.rpc.AbstractOperation = ro[methodName];

ro.addEventListener("result", doResults);

ro.addEventListener("fault", doFault);

var token:AsyncToken = null;

if(args && args.length >0){

token = op.send.apply(null,args);

}

else {

token = op.send();

}

token.eventName = eventName;

}

private function doResults(event:ResultEvent):void{

var e:DataManagerResultEvent = new DataManagerResultEvent(event.token.eventName, event.result);

this.dispatchEvent(e);

}

private function doFault(fault:FaultEvent):void{

this.dispatchEvent(fault);

}

public override function toString():String{

return "RemoteObjectDataManager";

}

}

}

/** PrivateClass is used to make DataManager constructor private */

class PrivateClass{

public function PrivateClass() {}

}

package com.ny.flex.centralManagement.manager
{
import com.ny.flex.centralManagement.event.DataManagerResultEvent;

import flash.events.EventDispatcher;

import mx.core.Application;
import mx.resources.ResourceManager;
import mx.rpc.AbstractOperation;
import mx.rpc.AsyncToken;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.remoting.mxml.RemoteObject;

public class RemoteObjectManager extends EventDispatcher {
public var ro:RemoteObject;
private var eventName:String;

private static var instanceMap:Object = new Object();

public function RemoteObjectManager(pri:PrivateClass,dest:String){
this.ro = new RemoteObject();
ro.destination = dest;
}

public static function getRemoteObjectManager(dest:String):RemoteObjectManager{
if(RemoteObjectManager.instanceMap[dest] == null){
RemoteObjectManager.instanceMap[dest] = new RemoteObjectManager(new PrivateClass(),dest);
}
var dm:RemoteObjectManager= RemoteObjectManager.instanceMap[dest];
return dm;
}
public function makeRemoteCall(methodName:String,eventName:String,args:Array=null):void{
this.eventName = eventName;
var op:mx.rpc.AbstractOperation = ro[methodName];
ro.addEventListener("result", doResults);
ro.addEventListener("fault", doFault);
var token:AsyncToken = null;
if(args && args.length >0){
token = op.send.apply(null,args);
}
else {
token = op.send();
}
token.eventName = eventName;
}
private function doResults(event:ResultEvent):void{
var e:DataManagerResultEvent = new DataManagerResultEvent(event.token.eventName, event.result);
this.dispatchEvent(e);
}
private function doFault(fault:FaultEvent):void{
this.dispatchEvent(fault);
}

public override function toString():String{
return "RemoteObjectDataManager";
}
}
}
/**   PrivateClass is used to make    DataManager constructor private  */
class PrivateClass{
public function PrivateClass() {}

}


“GlobalObjectManager”用来在UI之间传递信息,例如,我们使用ViewStack的selectedIndex来决定显示ViewStack中的哪一个视图,则使用全局对象viewStackSelectedIndex ,其代码如下面的黑体部分:

Xml代码

<?xml version=”1.0″ encoding=”utf-8″?>

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml“ xmlns:views=”com.ny.flex.centralManagement.views.*” layout=”absolute” width=”100%” height=”100%”>

<mx:Script>

<![CDATA[

import mx.binding.utils.BindingUtils;

import com.ny.flex.centralManagement.manager.GlobalObjectManager;

[Bindable]

public var gom:GlobalObjectManager=GlobalObjectManager.getInstance();

]]>

</mx:Script>

<mx:HBox horizontalAlign=”center” verticalAlign=”top” width=”100%” height=”100%” y=”0″ x=”0″>

<mx:ViewStack id=”viewStack” resizeToContent=”true” selectedIndex=”{gom.viewStackSelectedIndex}” >

<views:LoginView />

<views:BuddyListView/>

</mx:ViewStack>

</mx:HBox>

</mx:Application>

<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml“  xmlns:views=”com.ny.flex.centralManagement.views.*” layout=”absolute”  width=”100%” height=”100%”>
<mx:Script>
<![CDATA[
import mx.binding.utils.BindingUtils;
import com.ny.flex.centralManagement.manager.GlobalObjectManager;
[Bindable]
public  var gom:GlobalObjectManager=GlobalObjectManager.getInstance();
]]>
</mx:Script>
<mx:HBox  horizontalAlign=”center” verticalAlign=”top”  width=”100%” height=”100%” y=”0″ x=”0″>
<mx:ViewStack id=”viewStack”  resizeToContent=”true” selectedIndex=”{gom.viewStackSelectedIndex}” >
<views:LoginView  />
<views:BuddyListView/>
</mx:ViewStack>
</mx:HBox>
</mx:Application>


再回头看看在

LoginService 代码中的loginHandler方法,在此viewStackSelectedIndex 全局对象被更新。

LoginService

代码中的loginHandler方法,在此viewStackSelectedIndex 全局对象被更新。

Xml代码

private function loginHandler(event:DataManagerResultEvent):void {

var userName:String = event.result as String;

if(userName){

gom.loginUserName = userName;

gom.viewStackSelectedIndex=1;

}

}

private function loginHandler(event:DataManagerResultEvent):void {
var userName:String = event.result as String;
if(userName){
gom.loginUserName = userName;
gom.viewStackSelectedIndex=1;
}
}


[Bindable]元标签使得其值的改变立刻生效。

BuddyList.mxml代码:

Xml代码

<?xml version=”1.0″ encoding=”utf-8″?>

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” title=”Buddy List of {gom.loginUserName}” creationComplete=”init()” width=”500″ height=”320″>

<mx:Script>

<![CDATA[

import com.ny.flex.centralManagement.service.BuddyService;

import com.ny.flex.centralManagement.manager.GlobalObjectManager;

import mx.collections.ArrayCollection;

import mx.rpc.events.ResultEvent;

[Bindable]

public var gom:GlobalObjectManager = GlobalObjectManager.getInstance();

private function init():void{

BuddyService.getInstance().getBuddyList();

}

]]>

</mx:Script>

<mx:DataGrid id=”buddyList” dataProvider=”{gom.mybuddyList}” borderStyle=”none” width=”100%” height=”100%” >

<mx:columns>

<mx:DataGridColumn dataField=”firstName” headerText=”First Name”/>

<mx:DataGridColumn dataField=”lastName” headerText=”Last Name”/>

</mx:columns>

</mx:DataGrid>

</mx:Panel>

<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” title=”Buddy List of {gom.loginUserName}” creationComplete=”init()” width=”500″ height=”320″>
<mx:Script>
<![CDATA[
import com.ny.flex.centralManagement.service.BuddyService;
import com.ny.flex.centralManagement.manager.GlobalObjectManager;
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
[Bindable]
public var gom:GlobalObjectManager = GlobalObjectManager.getInstance();

private function init():void{
BuddyService.getInstance().getBuddyList();
}
]]>
</mx:Script>

<mx:DataGrid id=”buddyList”  dataProvider=”{gom.mybuddyList}”  borderStyle=”none” width=”100%” height=”100%” >
<mx:columns>
<mx:DataGridColumn dataField=”firstName” headerText=”First Name”/>
<mx:DataGridColumn dataField=”lastName” headerText=”Last Name”/>
</mx:columns>

</mx:DataGrid>
</mx:Panel>


BuddyService是另一个服务类代码用来和远程对象通讯。

这是最符合本人喜好的Flex程序的框架结构。它的优点是非常的清晰,没有累赘的发送和监听事件的工作,并且代码非常容易维护。遗憾的是,在此还没有获得足够的理论支持这一框架理论。

再来看看MVC框架的代表:MVC-Cairngorm。

Flex 开发架构(二): 中央管理-Flex Central Managerment

有没有听说过这个奇怪的词汇:“Cairngorm”?如果你的回答是“No” && 你是Flex程序员,哪你就看看自己是不是住在一个井底。J

Cairngorm是Flex的一个MVC框架结构,名字取自苏格兰的一个山脉。(kao,如果是我建立一个自己的框架结构,我就取名叫:“天安门”。)

有关这个框架,在网络上有很多图表用来讨论。下面是我所理解的框架图表:



使用Cairngorm的第一步是建立框架结构的骨架,包括了三个对象:

Model Locater
Service Locator
Front Controller

Model Locator:承载了组件之间的所有的传递的信息和数据,这是一个Bindable(可绑定的)对象。

Service Locator:定义了与数据源(Httpservice,Webservice,Remoteobject)之间通讯的界面。

Front Controller:建立播送事件(Dispatch event)和命令层(command)之间的对应关系(mapping)。

看一下相关的代码:

BuddyAppModelLocator.as:

Xml代码

<em>package com.ny.flex.cairngorm.model

{

import com.ny.flex.cairngorm.vo.User;

import mx.collections.ArrayCollection;

[Bindable]

public class BuddyAppModelLocator

{

public var buddyList:ArrayCollection=new ArrayCollection();

public var loginUser:User=new User();

public var viewStackSelectedIndex :int = 0;

static private var __instance:BuddyAppModelLocator=null;

static public function getInstance():BuddyAppModelLocator

{

if(__instance == null)

{

__instance=new BuddyAppModelLocator();

}

return __instance;

}

}

}

</em>

package com.ny.flex.cairngorm.model
{
import com.ny.flex.cairngorm.vo.User;

import mx.collections.ArrayCollection;

[Bindable]
public class BuddyAppModelLocator
{
public var buddyList:ArrayCollection=new ArrayCollection();
public var loginUser:User=new User();
public var viewStackSelectedIndex :int = 0;

static private var __instance:BuddyAppModelLocator=null;

static public function getInstance():BuddyAppModelLocator
{
if(__instance == null)
{
__instance=new BuddyAppModelLocator();
}
return __instance;
}
}
}


在Model Locator代码中,定义了三个public的变量,buddyList:用来存放由数据库获取的密友列表;loginUser:定义一个User类型对象;viewStackSelectedIndex:定义viewStack指向的视窗。
几乎所有的服务层返回的信息都需要在Model Locator中有一个相应的对象。

BuddyServiceLocator.mxml:

Xml代码

<em><em><?xml version=”1.0″ encoding=”utf-8″?>

<cairngorm:ServiceLocator xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:cairngorm=”http://www.adobe.com/2006/cairngorm“>

<mx:RemoteObject id=”buddyRo“ destination=”flexmvcRO” >

</mx:RemoteObject>

</cairngorm:ServiceLocator>

</em></em>

<?xml version=”1.0″ encoding=”utf-8″?>
<cairngorm:ServiceLocator xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:cairngorm=”http://www.adobe.com/2006/cairngorm“>
<mx:RemoteObject id=”buddyRo“  destination=”flexmvcRO” >

</mx:RemoteObject>
</cairngorm:ServiceLocator>


上述代码定义了程序将要调用的RemoteObject ,RemoteObject 所调用的Destination需要和remote_config.xml文件中的Destination相一致。在此,Destination的值为“flexmvcRO”。

BuddyListController.as:

Xml代码

<em><em><strong>package com.ny.flex.cairngorm.control

{

import com.adobe.cairngorm.control.FrontController;

import com.ny.flex.cairngorm.command.GetBuddyListCommand;

import com.ny.flex.cairngorm.command.LoginCommand;

import com.ny.flex.cairngorm.event.GetBuddyListEvent;

import com.ny.flex.cairngorm.event.LoginEvent;

public class BuddyListController extends FrontController

{

public function BuddyListController()

{

super();

addCommand(LoginEvent.LOGIN_EVENT,LoginCommand);

addCommand(GetBuddyListEvent.GET_BUDDY_LIST_EVENT,

GetBuddyListCommand);

}

}

}

</strong></em></em>

package com.ny.flex.cairngorm.control
{
import com.adobe.cairngorm.control.FrontController;
import com.ny.flex.cairngorm.command.GetBuddyListCommand;
import com.ny.flex.cairngorm.command.LoginCommand;
import com.ny.flex.cairngorm.event.GetBuddyListEvent;
import com.ny.flex.cairngorm.event.LoginEvent;

public class BuddyListController extends FrontController
{
public function BuddyListController()
{
super();
addCommand(LoginEvent.LOGIN_EVENT,LoginCommand);
addCommand(GetBuddyListEvent.GET_BUDDY_LIST_EVENT,
GetBuddyListCommand);
}

}
}


很显然,上述的Controller代码是事件和命令的对应处理的地方。

如何能将这些乱七八糟的东西结合在一起?其Magic的地方是在主页(Main application)上,代码如下:

[b]BuddList_Main_Cairngorm.mxml:[/b]

Xml代码

<em><em><strong><?xml version=”1.0″ encoding=”utf-8″?>

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml“ xmlns:service=”com.ny.flex.cairngorm.service.*“ xmlns:controller=”com.ny.flex.cairngorm.control.*” xmlns:views=”com.ny.flex.cairngorm.views.*” layout=”absolute“ width=”100%” height=”100%“>

<mx:Script>

<![CDATA[

import com.ny.flex.cairngorm.model.BuddyAppModelLocator;

[Bindable]

public var myModel:BuddyAppModelLocator = BuddyAppModelLocator.getInstance();

]]>

</mx:Script>

<service:BuddyServiceLocator id=”myservice“/>

<controller:BuddyListController id=”myController“/>

<mx:HBox horizontalAlign=”center” verticalAlign=”top“ width=”100%” height=”100%” y=”0” x=”0“>

<mx:ViewStack id=”viewStack“ resizeToContent=”true” selectedIndex=”{myModel.viewStackSelectedIndex}” >

<views:LoginView />

<views:BuddyListView/>

</mx:ViewStack>

</mx:HBox>

</mx:Application>

</strong></em></em>

<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml“  xmlns:service=”com.ny.flex.cairngorm.service.*“  xmlns:controller=”com.ny.flex.cairngorm.control.*” xmlns:views=”com.ny.flex.cairngorm.views.*” layout=”absolute“  width=”100%” height=”100%“>
<mx:Script>
<![CDATA[
import com.ny.flex.cairngorm.model.BuddyAppModelLocator;
[Bindable]
public var myModel:BuddyAppModelLocator = BuddyAppModelLocator.getInstance();
]]>
</mx:Script>

<service:BuddyServiceLocator id=”myservice“/>
<controller:BuddyListController id=”myController“/>

<mx:HBox  horizontalAlign=”center” verticalAlign=”top“  width=”100%” height=”100%” y=”0” x=”0“>
<mx:ViewStack id=”viewStack“  resizeToContent=”true” selectedIndex=”{myModel.viewStackSelectedIndex}” >
<views:LoginView  />
<views:BuddyListView/>
</mx:ViewStack>
</mx:HBox>
</mx:Application>


现在用户可以建立视图组件,并从这些组件中播送事件:

[b]LoginView.mxml:[/b]

Xml代码

<em><em><strong><em> <![CDATA[

import com.ny.flex.cairngorm.event.LoginEvent;

import com.ny.flex.cairngorm.vo.User;

import mx.validators.Validator;

private function login():void{

if(Validator.validateAll(validators).length == 0){

var loginUser:User = new User();

loginUser.userName=username.text;

loginUser.password=password.text;

var loginEvent:LoginEvent = new LoginEvent();

loginEvent.loginUser = loginUser;

loginEvent.dispatch();

}

}

]]>

</mx:Script>

<!– Validators–>

<mx:Array id=”validators“>

<mx:StringValidator id=”userNameValidator” source=”{username}“ property=”text“ required=”true“/>

<mx:StringValidator id=”passwordValidator” source=”{password}“ property=”text” required=”true” />

</mx:Array>

<mx:Form id=”loginForm” x=”0” y=”0“>

<mx:FormItem label=”Username:” >

<mx:TextInput id=”username” />

</mx:FormItem>

<mx:FormItem label=”Password:” >

<mx:TextInput id=”password” displayAsPassword=”true” />

</mx:FormItem>

<mx:FormItem direction=”horizontal” verticalGap=”15” paddingTop=”5” width=”170“>

<mx:Button id=”loginBtn” label=”Login” click=”login()”/>

</mx:FormItem>

</mx:Form>

</mx:Panel>

</em></strong></em></em>

      <![CDATA[
import com.ny.flex.cairngorm.event.LoginEvent;
import com.ny.flex.cairngorm.vo.User;
import mx.validators.Validator;
private function login():void{
if(Validator.validateAll(validators).length == 0){
var loginUser:User = new User();
loginUser.userName=username.text;
loginUser.password=password.text;
var loginEvent:LoginEvent = new LoginEvent();
loginEvent.loginUser = loginUser;
loginEvent.dispatch();
}
}
]]>
</mx:Script>

<!–  Validators–>
<mx:Array id=”validators“>
<mx:StringValidator  id=”userNameValidator” source=”{username}“  property=”text“  required=”true“/>
<mx:StringValidator  id=”passwordValidator” source=”{password}“  property=”text” required=”true” />
</mx:Array>

<mx:Form id=”loginForm” x=”0” y=”0“>
<mx:FormItem label=”Username:” >
<mx:TextInput id=”username” />
</mx:FormItem>
<mx:FormItem label=”Password:” >
<mx:TextInput id=”password” displayAsPassword=”true” />
</mx:FormItem>
<mx:FormItem direction=”horizontal” verticalGap=”15” paddingTop=”5” width=”170“>
<mx:Button id=”loginBtn” label=”Login” click=”login()”/>
</mx:FormItem>
</mx:Form>

</mx:Panel>


每一个动作都需要建立一个相应的事件:

[b]LoginEvent.as:[/b]

Xml代码

<em><em><strong><em>package com.ny.flex.cairngorm.event

{

import com.adobe.cairngorm.control.CairngormEvent;

import com.ny.flex.cairngorm.vo.User;

import flash.events.Event;

public class LoginEvent extends CairngormEvent

{

public static var LOGIN_EVENT:String = “loginEvent”

public var loginUser:User ;

public function LoginEvent()

{

super(LOGIN_EVENT);

}

override public function clone() : Event

{

return new LoginEvent();

}

}

}

</em></strong></em></em>

package com.ny.flex.cairngorm.event
{
import com.adobe.cairngorm.control.CairngormEvent;
import com.ny.flex.cairngorm.vo.User;

import flash.events.Event;

public class LoginEvent extends CairngormEvent
{
public static var LOGIN_EVENT:String = “loginEvent”
public  var  loginUser:User ;

public function LoginEvent()
{
super(LOGIN_EVENT);
}

override public function clone() : Event
{
return new LoginEvent();
}
}
}


每一个事件都要对应于一个命令:
[b]LoginCommand.as:[/b]

Xml代码

<em><em><strong>package com.ny.flex.cairngorm.command

{

import com.adobe.cairngorm.commands.ICommand;

import com.adobe.cairngorm.control.CairngormEvent;

import com.ny.flex.cairngorm.event.LoginEvent;

import com.ny.flex.cairngorm.model.BuddyAppModelLocator;

import com.ny.flex.cairngorm.service.LoginDelegate;

import com.ny.flex.cairngorm.vo.User;

import mx.controls.Alert;

import mx.rpc.IResponder;

public class LoginCommand implements ICommand, IResponder

{

public function LoginCommand()

{

}

public function execute(event:CairngormEvent):void

{

var loginEvent:LoginEvent = LoginEvent(event);

var user:User = loginEvent.loginUser;

var lgoinService :LoginDelegate

= new LoginDelegate(this);

lgoinService.authenticate(user);

}

public function result(event:Object):void

{

var authUser:User = User(event.result);

BuddyAppModelLocator.getInstance().loginUser = authUser;

BuddyAppModelLocator.getInstance().viewStackSelectedIndex=1;

}

public function fault(info:Object):void

{

Alert.show(“Login Fail Error “);

}

}

}

</strong></em></em>

package com.ny.flex.cairngorm.command
{
import com.adobe.cairngorm.commands.ICommand;
import com.adobe.cairngorm.control.CairngormEvent;
import com.ny.flex.cairngorm.event.LoginEvent;
import com.ny.flex.cairngorm.model.BuddyAppModelLocator;
import com.ny.flex.cairngorm.service.LoginDelegate;
import com.ny.flex.cairngorm.vo.User;

import mx.controls.Alert;
import mx.rpc.IResponder;

public class LoginCommand implements ICommand, IResponder
{
public function LoginCommand()
{
}

public function execute(event:CairngormEvent):void
{
var loginEvent:LoginEvent = LoginEvent(event);
var user:User = loginEvent.loginUser;
var lgoinService :LoginDelegate
=  new LoginDelegate(this);
lgoinService.authenticate(user);
}

public function result(event:Object):void
{
var authUser:User = User(event.result);
BuddyAppModelLocator.getInstance().loginUser = authUser;
BuddyAppModelLocator.getInstance().viewStackSelectedIndex=1;
}

public function fault(info:Object):void
{
Alert.show(“Login Fail Error “);
}

}
}


然后,在Front Controller(前端控制器)中build对应关系:

[b]addCommand(LoginEvent.LOGIN_EVENT,LoginCommand);[/b]

命令层需要完成商务逻辑,用户需要在执行方法中加入商务逻辑代码:

Xml代码

<em><em><strong> var lgoinService :LoginDelegate =

new LoginDelegate(this);

lgoinService.authenticate(user);

</strong></em></em>

                  var lgoinService :LoginDelegate =
new LoginDelegate(this);
lgoinService.authenticate(user);


Delegate(代表)用来通过服务层(Service Locator)调用数据源:

[b]LoginDelegate.as:[/b]

Xml代码

<em><em><strong><em>package com.ny.flex.cairngorm.service

{

import com.adobe.cairngorm.business.ServiceLocator;

import com.ny.flex.cairngorm.vo.User;

import mx.rpc.IResponder;

public class LoginDelegate

{

private var responder:IResponder;

private var service:Object;

public function LoginDelegate(responder :IResponder){

this.service =

ServiceLocator.getInstance()

.getRemoteObject(“buddyRo”);

this.responder = responder;

}

public function authenticate(user:User):void{

var call:Object = service.authenticate(user);

call.addResponder(responder);

}

}

}

</em></strong></em></em>

package com.ny.flex.cairngorm.service
{
import com.adobe.cairngorm.business.ServiceLocator;
import com.ny.flex.cairngorm.vo.User;

import mx.rpc.IResponder;

public class LoginDelegate
{
private var responder:IResponder;
private var service:Object;

public function LoginDelegate(responder :IResponder){
this.service =
ServiceLocator.getInstance()
.getRemoteObject(“buddyRo”);
this.responder = responder;
}

public function  authenticate(user:User):void{
var call:Object = service.authenticate(user);
call.addResponder(responder);
}
}
}



返回的结果将回复到命令层(LoginCommand.as)的结果方法中,在此方法中Model被更新,然后数据被绑定到结果视图上:
[b]LoginCommand.as:[/b]

Xml代码

<em><em><strong> public function result(event:Object):void

{

var authUser:User = User(event.result);

BuddyAppModelLocator.getInstance().loginUser

= authUser;

BuddyAppModelLocator.getInstance().viewStackSelectedIndex=1;

}

</strong></em></em>

            public function result(event:Object):void
{
var authUser:User = User(event.result);
BuddyAppModelLocator.getInstance().loginUser
= authUser;
BuddyAppModelLocator.getInstance().viewStackSelectedIndex=1;
}


其它的视图工作流程同上,整个密友列表项目的结构如下图所示:




使用Cairngorm开发应用项目Layer,测试性高。并且使得程序员更专业化。
但这个框架的确很不容易学习和维护,那么有没有更好的方法简化它?

来看看:咔嚓Front ControllerCairngorm

Flex 开发架构(四): 去除FrontController 的Cairngorm

正如在使用Cairngorm时,视图中的每一个动作都播送一个事件,每个播送出去的事件都需要建立相应的命令代码来处理事件。并且需要在FrontController中对应他们的关系。例如下面的简单流程:
Loginvew.xml
—> action login() —>dispatch LoginEvent —>Handled by
LoginCommand —>mapping LoginEvent and LoginCommand in FrontController
.
在不考虑商务层代码的情况下,当用户建立然后新的动作时,就需要建立2个新的代码,修改Controller代码。
那么如何简化Cairngorm,我的方法是去掉FrontController,取而代之的是Service Façade,其框架图如下所示:


在上图中,视图不再播送如何事件,而是直接调用Service Façade。而Service Façade则直接调用商务委托层与服务器端通讯(如Remote Object等),然后商务委托处理结果并更新Model Locator,最后Model Locator通过绑定(Binding)更新视窗中的结果。

看一下被改变的代码:
ServiceFacade.as:

Xml代码

package com.ny.flex.cairngorm.no_fc.service

{

import com.ny.flex.cairngorm.no_fc.vo.User;

public class ServiceFacade

{

private static var _serviceFacade:ServiceFacade = null;

public function ServiceFacade(privateClass:PrivateClass)

{

if(ServiceFacade._serviceFacade == null){

ServiceFacade._serviceFacade = this;

}

}

public static function getInstance():ServiceFacade {

if(_serviceFacade == null){

_serviceFacade = new ServiceFacade(new PrivateClass);

}

return _serviceFacade;

}

public function authenticate(user:User):void{

LoginDelegate.getInstance().authenticate(user);

}

public function getBuddyList():void{

BuddyListDelegate.getInstance().getBuddyList();

}

}

}

class PrivateClass{}

package com.ny.flex.cairngorm.no_fc.service
{
import com.ny.flex.cairngorm.no_fc.vo.User;

public class ServiceFacade
{
private static var _serviceFacade:ServiceFacade = null;

public function ServiceFacade(privateClass:PrivateClass)
{
if(ServiceFacade._serviceFacade == null){
ServiceFacade._serviceFacade = this;
}
}
public static function getInstance():ServiceFacade {
if(_serviceFacade == null){
_serviceFacade = new ServiceFacade(new PrivateClass);
}
return _serviceFacade;
}

public function authenticate(user:User):void{
LoginDelegate.getInstance().authenticate(user);
}
public function  getBuddyList():void{
BuddyListDelegate.getInstance().getBuddyList();
}
}
}

class  PrivateClass{}


ServiceFacade提供了对所有商务逻辑为一体的界面,视窗的行为(Action)仅仅调用façade,例如下面代码中的Login的动作。
Loginvew.xml :

Xml代码

private function login():void{

if(Validator.validateAll(validators).length == 0){

var loginUser:User = new User();

loginUser.userName=username.text;

loginUser.password=password.text;

<span style="color: rgb(255, 102, 0);"><strong><em>serviceFacade.authenticate(loginUser);</em></strong></span>

}

}

private function login():void{
if(Validator.validateAll(validators).length == 0){
var loginUser:User = new User();
loginUser.userName=username.text;
loginUser.password=password.text;
serviceFacade.authenticate(loginUser);
}
}


函数 serviceFacade.authenticate(loginUser)如下:

Xml代码

<strong> public function authenticate(user:User):void{

<span style="color: rgb(255, 102, 0);"><strong><em>LoginDelegate.</em></strong></span>getInstance().authenticate(user);

}

</strong>

		public function authenticate(user:User):void{
[b]LoginDelegate.getInstance().authenticate(user);
}
[/b]


[b]ServiceFacade[/b]使用[b]LoginDelegate 来真正实现Business Logic:[/b]

LoginDelegate.as:

Xml代码

<strong>package com.ny.flex.cairngorm.no_fc.service

{

import com.ny.flex.cairngorm.no_fc.*;

import com.ny.flex.cairngorm.no_fc.vo.User;

import mx.rpc.IResponder;

import mx.rpc.Responder;

import mx.rpc.events.ResultEvent;

public class LoginDelegate extends BaseDelegate

{

private static var _loginDelegate:LoginDelegate = null;

public function LoginDelegate(privateClass:PrivateClass){

if(LoginDelegate._loginDelegate == null ){

LoginDelegate._loginDelegate = this;

}

}

public static function getInstance():LoginDelegate{

if(_loginDelegate == null){

_loginDelegate = new LoginDelegate(new PrivateClass);

}

return _loginDelegate;

}

public function <span style="color: rgb(255, 102, 0);"><strong><em> authenticate</em></strong></span>(user:User):void{

var responder:IResponder = new Responder(onResult_Authenticate,fault);

var call:Object = service.authenticate(user);

call.addResponder(responder);

}

private function onResult_Authenticate(event:ResultEvent):void{

var authUser:User = event.result as User;

model.loginUser = authUser;

model.viewStackSelectedIndex = 1;

}

}

}

class PrivateClass{}</strong>

package com.ny.flex.cairngorm.no_fc.service
{
import com.ny.flex.cairngorm.no_fc.*;
import com.ny.flex.cairngorm.no_fc.vo.User;

import mx.rpc.IResponder;
import mx.rpc.Responder;
import mx.rpc.events.ResultEvent;

public class LoginDelegate extends BaseDelegate
{
private static  var _loginDelegate:LoginDelegate = null;

public function LoginDelegate(privateClass:PrivateClass){
if(LoginDelegate._loginDelegate == null ){
LoginDelegate._loginDelegate = this;
}

}

public  static function  getInstance():LoginDelegate{
if(_loginDelegate == null){
_loginDelegate = new LoginDelegate(new PrivateClass);
}
return  _loginDelegate;
}

public function [b] authenticate(user:User):void{
var responder:IResponder = new Responder(onResult_Authenticate,fault);
var call:Object = service.authenticate(user);
call.addResponder(responder);
}

private function onResult_Authenticate(event:ResultEvent):void{
var authUser:User = event.result as User;
model.loginUser = authUser;
model.viewStackSelectedIndex = 1;
}

}
}

class PrivateClass{}[/b]


上面的[b]authenticate(user) 实现于后台通讯并[/b]处理结果,更新Model Locator

其他的视图和相应的处理方法 与上面类似。

这个解决方案简化了Cairngorm的框架,使得代码更易于理解。而且,彻底摆脱了 无味的dispatcher,frontcontroller 和command。 编码效率会提高 并且易于调试。

但其弱点是显而易见的:它违犯了解耦的(就是使得设计程序耦合性尽可能的降低)设计规范,使得界面上的行为(Action)和Serivce Façade产生了耦合关系。

在最后一篇中我将讨论下一个Flex 开发的热点:Mate-标签化的框架。

Flex 开发架构(五): Mate-基于标签的框架

Mate 将会成为Flex领域的另一个热点。它使用设置(configuration)来调用Service,处理结果,同样也使用设置文件来更新绑定对象(Bindable object)。从某个角度来说:Mate是Flex领域的“springframework”。

Mate有两个架构层面的图表。一个是来自Yakov Fain of Farata Systems,另一个来自ASFusion。我更喜欢后者,其构架图如下:

http://mate.asfusion.com/assets/content/diagrams/two_way_view_injection.png


来看看用Mate的编程方式来建立buddyList应用程序。

1,建立Mate的核心组件:EventMap。

EventMap是Mate的心脏,它黏着了所有的组件和控件。其代码如下:

BuddyListEventMap.mxml:

Xml代码

<?xml version=”1.0″ encoding=”utf-8″?>

<EventMap xmlns=”http://mate.asfusion.com/”

xmlns:mx=http://www.adobe.com/2006/mxml>

</EventMap>

<?xml version=”1.0″ encoding=”utf-8″?>

<EventMap xmlns=”http://mate.asfusion.com/”
xmlns:mx=http://www.adobe.com/2006/mxml>

</EventMap>


其它内容稍后再填写,现在我们需要告诉主程序(Main Application)初始化EventMap:

主程序 Flex_Mate.mxml:

Xml代码

<?xml version="1.0" encoding="utf-8"?>

<mx:Application xmlns:map="com.ny.flex.mate.map.*" xmlns:views="com.ny.flex.mate.views.*" xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">

<mx:Script>

<![CDATA[

[Bindable]

public var viewStackSelectedIndex :int = 0;

]]>

</mx:Script>

<span style="color: rgb(255, 102, 0);"><strong><em><map:BuddyListEventMap/></em></strong></span>

<mx:HBox horizontalAlign="center" verticalAlign="top" width="100%" height="100%" y="0" x="0">

<mx:ViewStack id="viewStack" resizeToContent="true" selectedIndex="{viewStackSelectedIndex}" >

<views:LoginView />

<views:BuddyListView/>

</mx:ViewStack>

</mx:HBox>

</mx:Application>

<?xml version="1.0" encoding="utf-8"?>
<mx:Application  xmlns:map="com.ny.flex.mate.map.*" xmlns:views="com.ny.flex.mate.views.*" xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
[Bindable]
public var viewStackSelectedIndex :int = 0;
]]>
</mx:Script>
<map:BuddyListEventMap/>
<mx:HBox  horizontalAlign="center" verticalAlign="top"  width="100%" height="100%" y="0" x="0">
<mx:ViewStack id="viewStack"  resizeToContent="true" selectedIndex="{viewStackSelectedIndex}" >
<views:LoginView  />
<views:BuddyListView/>
</mx:ViewStack>
</mx:HBox>
</mx:Application>


2. 建立LoginView :

Xml代码

<?xml version="1.0" encoding="utf-8"?>

<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="300" height="200" horizontalAlign="center" verticalAlign="middle" title="Flex Cirngorm Login">

<mx:Script>

<![CDATA[

import com.ny.flex.mate.event.LoginEvent;

import com.ny.flex.mate.vo.User;

import mx.validators.Validator;

private function login():void{

if(Validator.validateAll(validators).length == 0){

var loginUser:User = new User();

loginUser.userName=username.text;

loginUser.password=password.text;

<span style="color: rgb(255, 102, 0);"><strong><em>var loginEvent:LoginEvent = new LoginEvent(LoginEvent.LOGIN);

loginEvent.loginUser = loginUser;

dispatchEvent(loginEvent);</em></strong></span>

}

}

]]>

</mx:Script>

<!-- Validators-->

<mx:Array id="validators">

<mx:StringValidator id="userNameValidator" source="{username}" property="text" required="true"/>

<mx:StringValidator id="passwordValidator" source="{password}" property="text" required="true" />

</mx:Array>

<mx:Form id="loginForm" x="0" y="0">

<mx:FormItem label="Username:" >

<mx:TextInput id="username" />

</mx:FormItem>

<mx:FormItem label="Password:" >

<mx:TextInput id="password" displayAsPassword="true" />

</mx:FormItem>

<mx:FormItem direction="horizontal" verticalGap="15" paddingTop="5" width="170">

<mx:Button id="loginBtn" label="Login" click="login()"/>

</mx:FormItem>

</mx:Form>

</mx:Panel>

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="300" height="200" horizontalAlign="center" verticalAlign="middle" title="Flex Cirngorm Login">
<mx:Script>
<![CDATA[
import com.ny.flex.mate.event.LoginEvent;
import com.ny.flex.mate.vo.User;
import mx.validators.Validator;
private function login():void{
if(Validator.validateAll(validators).length == 0){
var loginUser:User = new User();
loginUser.userName=username.text;
loginUser.password=password.text;
var loginEvent:LoginEvent = new LoginEvent(LoginEvent.LOGIN);
loginEvent.loginUser = loginUser;
dispatchEvent(loginEvent);
}
}
]]>
</mx:Script>

<!--  Validators-->
<mx:Array id="validators">
<mx:StringValidator  id="userNameValidator" source="{username}"  property="text"  required="true"/>
<mx:StringValidator  id="passwordValidator" source="{password}"  property="text" required="true" />
</mx:Array>

<mx:Form id="loginForm" x="0" y="0">
<mx:FormItem label="Username:" >
<mx:TextInput id="username" />
</mx:FormItem>
<mx:FormItem label="Password:" >
<mx:TextInput id="password" displayAsPassword="true" />
</mx:FormItem>
<mx:FormItem direction="horizontal" verticalGap="15" paddingTop="5" width="170">
<mx:Button id="loginBtn" label="Login" click="login()"/>
</mx:FormItem>
</mx:Form>

</mx:Panel>


从上面可以看出在方法login()中发送(dispatch)了LoginEvent, 来看看LoginEvent代码:

Xml代码

package com.ny.flex.mate.event

{

import com.ny.flex.mate.vo.User;

import flash.events.Event;

public class LoginEvent extends Event

{

public static const LOGIN:String = "login";

public var loginUser:User;

public function LoginEvent(type:String, <span style="color: rgb(255, 102, 0);"><strong><em>bubbles:Boolean=true</em></strong></span>, cancelable:Boolean=false)

{

super(type, bubbles, cancelable);

}

}

}

package com.ny.flex.mate.event
{
import com.ny.flex.mate.vo.User;

import flash.events.Event;

public class LoginEvent extends Event
{
public static const LOGIN:String = "login";
public var  loginUser:User;
public function LoginEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}

}
}


在代码中“bubbles”属性必须为“true”,以使得上层的组件(EventMap)可以处理它。

Mate的魔法完全存在于EventMap中,LoginEvent在这里被标签化处理:

Xml代码

…..

<EventHandlers type=”{LoginEvent.LOGIN}“>

<RemoteObjectInvoker destination=”flexmvcRO”

method=”authenticate”

arguments=”{event.loginUser}“>

<resultHandlers>

<MethodInvoker generator=”{<span style="color: rgb(255, 102, 0);"><strong><em>LoginService</em></strong></span>}”

method=”<span style="color: rgb(255, 0, 0);"><strong><em>onResult_Authenticate</em></strong></span>”

arguments=”{<span style="color: rgb(128, 0, 0);"><strong><em>resultObject</em></strong></span>}“/>

</resultHandlers>

</RemoteObjectInvoker>

</EventHandlers>

……

…..

<EventHandlers type=”{LoginEvent.LOGIN}“>
<RemoteObjectInvoker destination=”flexmvcRO”
method=”authenticate”
arguments=”{event.loginUser}“>

<resultHandlers>
<MethodInvoker generator=”{LoginService}”
method=”onResult_Authenticate”
arguments=”{resultObject}“/>
</resultHandlers>
</RemoteObjectInvoker>
</EventHandlers>
……


在EvevntHandler代码中,用户可以定义service函数(RemoteObjectInvoker),同时也定义了结果处理的类、方法和参数。

来看看 LoginService.as代码::

Xml代码

package com.ny.flex.mate.service

{

import com.ny.flex.mate.vo.User;

public class <span style="color: rgb(255, 102, 0);"><strong><em>LoginService</em></strong></span>

{

[Bindable]

public var authUserName:String;

[Bindable]

public var viewStackSelectedIndex:int ;

public function <span style="color: rgb(255, 0, 0);"><strong><em>onResult_Authenticate</em></strong></span>(<span style="color: rgb(128, 0, 0);"><strong><em>user:User</em></strong></span>):void{

authUserName = user.userName;

viewStackSelectedIndex = 1;

}

}

}

package com.ny.flex.mate.service
{
import com.ny.flex.mate.vo.User;

public class LoginService
{
[Bindable]
public var authUserName:String;
[Bindable]
public var viewStackSelectedIndex:int ;

public function  onResult_Authenticate(user:User):void{
authUserName = user.userName;
viewStackSelectedIndex = 1;
}

}
}


Service类处理结果,返回绑定的对象。然后我们就需要更新目标视窗。

Mate另一个闪光点就是注射(Injecting)可绑定的对象到目标视窗!用户只需要在EventMap类中增加另一个标签Injector。(Coolest 部分):

Xml代码

<Injectors target=”{BuddyListView}“>

<PropertyInjector targetKey=”authUserName”

source=”{LoginService}”

sourceKey=”authUserName“/>

</Injectors>

<Injectors target=”{Flex_Mate}“>

<PropertyInjector targetKey=”viewStackSelectedIndex”

source=”{LoginService}”

sourceKey=”viewStackSelectedIndex“/>

</Injectors>

<Injectors target=”{BuddyListView}“>
<PropertyInjector targetKey=”authUserName”
source=”{LoginService}”
sourceKey=”authUserName“/>

</Injectors>

<Injectors target=”{Flex_Mate}“>
<PropertyInjector targetKey=”viewStackSelectedIndex”
source=”{LoginService}”
sourceKey=”viewStackSelectedIndex“/>

</Injectors>


在此定义目标视窗、目标关键词,资源服务对象和资源关键词。 你根本不需要写任何glue code。

最后定义目标视窗: BuddyListView.mxml:

Xml代码

<?xml version="1.0" encoding="utf-8"?>

<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" title="Buddy List of {authUserName}" creationComplete="getBuddyList()" width="500" height="320">

<mx:Script>

<![CDATA[

import mx.collections.ArrayCollection;

import com.ny.flex.mate.event.GetBuddyListEvent;

[Bindable]

public var authUserName:String;

[Bindable]

public var buddyCollection:ArrayCollection;

private function getBuddyList():void{

var getBuddyListEvent:GetBuddyListEvent = new GetBuddyListEvent(GetBuddyListEvent.GET_BUDDY_LIST);

getBuddyListEvent.authUserName = authUserName;

dispatchEvent(getBuddyListEvent);

}

]]>

</mx:Script>

<mx:DataGrid id="buddyList" dataProvider="{buddyCollection}" borderStyle="none" width="100%" height="100%" >

<mx:columns>

<mx:DataGridColumn dataField="firstName" headerText="First Name"/>

<mx:DataGridColumn dataField="lastName" headerText="Last Name"/>

</mx:columns>

</mx:DataGrid>

</mx:Panel>

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" title="Buddy List of  {authUserName}" creationComplete="getBuddyList()" width="500" height="320">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import com.ny.flex.mate.event.GetBuddyListEvent;
[Bindable]
public var authUserName:String;
[Bindable]
public var buddyCollection:ArrayCollection;

private function getBuddyList():void{
var getBuddyListEvent:GetBuddyListEvent = new GetBuddyListEvent(GetBuddyListEvent.GET_BUDDY_LIST);
getBuddyListEvent.authUserName = authUserName;
dispatchEvent(getBuddyListEvent);
}
]]>
</mx:Script>

<mx:DataGrid id="buddyList"  dataProvider="{buddyCollection}"  borderStyle="none" width="100%" height="100%" >
<mx:columns>
<mx:DataGridColumn dataField="firstName" headerText="First Name"/>
<mx:DataGridColumn dataField="lastName" headerText="Last Name"/>
</mx:columns>

</mx:DataGrid>
</mx:Panel>


整个开发流程是这样的:

Action–>Dispatch Event–>Config Handler–>create service–>Inject Bindable Object –>another Action….

整个项目结构图 见附件

总结:

在我5篇blog中讨论过的Flex编程框架中,哪一个是最好的呢?

我认为中央管理(central management)最适合进阶水准的小型项目。因为无须学习新的框架,并且也一样有清晰的架构。 而且 他也是走向框架的起点。

对于Mate和Cairngorm,在我看来Mate略占上风,原因如下:

对于Cairngorm:

1,Cairngorm过于复杂,学习曲线较高

2,我觉得Cairngorm有一些垃圾代码(例如Frontcontroller,event 和Command)。

对于Mate:

1,比Cairngorm更简单易学,貌似继承和发扬了Flex的特质。

2,从EventMap中得益,因为无须编写在事件和服务之间的黏着代码。

但另一个方面, EventMap也会带来痛苦,试想一下,用户有50个行为和100个对象需要绑定,那就需要在EventMap中写入大量的configration代码。那么EventMap就成为一个灾难。

因此,如果用户使用Cairngorm,就可以选用 Cairngorm without FrontContoller的方案。

对于Mate,如果使用Meta标签来代替EventMap,就无须设置太多的东西,示例如下:

[EventHadler ={name ="myHandler", serviceclass="myservice" result , taget ...}]

MateDispatch(myevent).

我在期待着它的来临。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: