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>
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;
}
3.相关 pojo Friend.java:
Java代码
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: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>
现在,在login方法中发送请求:
Xml代码
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));
}
}
最后,在页面之间建立联系,在这里我使用播送事件:
Xml代码
dispatchEvent(new LoginUserEvent(username.text));
播送用户自定义的事件,就必须在代码最前面写入下述的元标签代码:
Xml代码
<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);
}
当登录成功后,密友列表页面将会显示,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);
}
然后完成事件监听的管理程序:
Xml代码
private function getUserName(event:LoginUserEvent):void{
loginUserName = event.loginUserName;
loginReq.getAllFriends.send();
}
发送获得全部密友列表的请求,并且创建对应的管理方法:
Xml代码
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>
使用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>
其功能的核心是:
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;
}
}
}
代码中有两个特别的对象:
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() {}
}
“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>
再回头看看在
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;
}
}
[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>
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>
在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>
上述代码定义了程序将要调用的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>
很显然,上述的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>
现在用户可以建立视图组件,并从这些组件中播送事件:
[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>
每一个动作都需要建立一个相应的事件:
[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>
每一个事件都要对应于一个命令:
[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>
然后,在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>
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>
返回的结果将回复到命令层(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>
其它的视图工作流程同上,整个密友列表项目的结构如下图所示:
使用Cairngorm开发应用项目Layer,测试性高。并且使得程序员更专业化。
但这个框架的确很不容易学习和维护,那么有没有更好的方法简化它?
来看看:咔嚓Front Controller的Cairngorm。
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{}
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>
}
}
函数 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>
[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>
上面的[b]authenticate(user) 实现于后台通讯并[/b]处理结果,更新Model Locator。
其他的视图和相应的处理方法 与上面类似。
这个解决方案简化了Cairngorm的框架,使得代码更易于理解。而且,彻底摆脱了 无味的dispatcher,frontcontroller 和command。 编码效率会提高 并且易于调试。
但其弱点是显而易见的:它违犯了解耦的(就是使得设计程序耦合性尽可能的降低)设计规范,使得界面上的行为(Action)和Serivce Façade产生了耦合关系。
在最后一篇中我将讨论下一个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>
其它内容稍后再填写,现在我们需要告诉主程序(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>
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>
从上面可以看出在方法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);
}
}
}
在代码中“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>
……
在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;
}
}
}
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>
在此定义目标视窗、目标关键词,资源服务对象和资源关键词。 你根本不需要写任何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>
整个开发流程是这样的:
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).
我在期待着它的来临。
在实际得开发中实现上述结构的方法非常之多。我只是将自己开发的渐变过程记录下来。希望对后来者有所借鉴。
这个系列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程序员,哪你就看看自己是不是住在一个井底。JCairngorm是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 Controller的Cairngorm。
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).
我在期待着它的来临。
相关文章推荐
- Flex 开发架构渐变
- Flex 开发架构渐变
- Flex 开发架构(五): Mate-基于标签的框架
- Flex 开发架构(五): Mate-基于标签的框架
- Flex 4架构的组件开发
- Flex 开发架构(三): MVC框架-Flex Cairngorm
- Flex企业应用开发实践学习笔记(八)——Flex on Java企业应用架构
- Flex 开发架构(一): 混沌未开-Flex-all-in-one
- flex跨平台移动化企业开发架构
- Flex 开发架构(五): Mate-基于标签的框架
- Flex 开发架构(三): MVC框架-Flex Cairngorm
- Flex 开发架构(三): MVC框架-Flex Cairngorm
- 关于Flex+json+Java的整合开发【连载一】- 整体架构框架介绍
- Flex 开发架构(四): 去除FrontController 的Cairngorm
- 架构相关:组件化/模块化/工程化/性能优化/开发规范与团队协作/组件间调用与通信(flex/redux)/调试与测试
- 关于Flex+json+Java的整合开发
- Android游戏开发学习(一):游戏的基本架构
- Elasticsearch全文检索企业开发记录总结(一):整体架构
- Android开发软件架构思考以及经验总结
- Flex开发环境的安装