Flex 开发架构渐变

 

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

flex and java

 

 

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

这个系列blog包括:

 

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

二。中央管理-Flex Central Managerment

三。MVC框架-Flex Cairngorm

四。咔嚓Front ControllerCairngorm

五。轮回转世-Mate

 

样例。

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

系统只有二个画面:

  登录-LoginView:

 

 login view

 

 

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

 

 buddylist

 

 

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

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

1。remot-config.xml:

 

Xml代码
  1. <?xml version=”1.0″ encoding=”UTF-8″?>  
  2. <service id=”remoting-service”  
  3.     class=”flex.messaging.services.RemotingService”>  
  4.   
  5.     <adapters>  
  6.         <adapter-definition id=”java-object” class=”flex.messaging.services.remoting.adapters.JavaAdapter” default=”true”/>  
  7.     </adapters>  
  8.   
  9.     <default-channels>  
  10.         <channel ref=”my-amf”/>  
  11.     </default-channels>  
  12.     <destination id=”flexmvcRO”>  
  13.      <properties>  
  14.       <source>com.ny.blog.flex.mvc.accessor.DummyAccessor</source>  
  15.       <scope>session</scope>  
  16.       </properties>  
  17.      <adapter ref=”java-object” />  
  18.     </destination>  
  19.   
  20. </service>  

 

2.DummyAccessor.java:

Java代码
  1. package com.ny.blog.flex.mvc.accessor;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. import com.ny.blog.flex.mvc.pojo.Friend;  
  7.   
  8. public class DummyAccessor {  
  9.   
  10.  public DummyAccessor() {  
  11.  }  
  12.    
  13.  public boolean login(String userName,String password){  
  14.   return true;  
  15.  }  
  16.    
  17.  public  List<Friend> getAllFriends(String userName){  
  18.   List<Friend> myBuddy = new ArrayList<Friend>();  
  19.   Friend dummy1 = new Friend();  
  20.   dummy1.setFirstName(”John”);  
  21.   dummy1.setLastName(”Smith”);  
  22.   myBuddy.add(dummy1);  
  23.     
  24.   Friend dummy2 = new Friend();  
  25.   dummy2.setFirstName(”Andy”);  
  26.   dummy2.setLastName(”Jones”);  
  27.   myBuddy.add(dummy2);  
  28.     
  29.   Friend dummy3 = new Friend();  
  30.   dummy3.setFirstName(”Michael”);  
  31.   dummy3.setLastName(”Niu”);  
  32.   myBuddy.add(dummy3);  
  33.   return myBuddy;  
  34.  }  
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代码
  1. public class Friend {  
  2.  private String firstName;  
  3.  private String lastName;  
  4.  private String nickName;  
  5.   
  6. public Friend() {  
  7.  }  
  8.   
  9. //getter and setters  
  10. …  
  11. }  
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代码
  1. <mx:Form id=”loginForm” x=”0″ y=”0″>  
  2.           <mx:FormItem label=”Username:” >  
  3.                <mx:TextInput id=”username” />  
  4.            </mx:FormItem>  
  5.            <mx:FormItem label=”Password:” >  
  6.                <mx:TextInput id=”password” displayAsPassword=”true” />  
  7.            </mx:FormItem>  
  8.            <mx:FormItem direction=”horizontal” verticalGap=”15″ paddingTop=”5″ width=”170″>  
  9.                <mx:Button id=”loginBtn” label=”Login” click=”login()”/>  
  10.            </mx:FormItem>  
  11.  </mx:Form>  

 

 

 

 

 

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

 

 

Xml代码
  1. <mx:RemoteObject  id=”loginReq” destination=”flexmvcRO”>  
  2.     <mx:method name=”login” result=”loginHandler(event)” fault=”mx.controls.Alert.show(event.fault.faultString)”>  
  3.      <mx:arguments>  
  4.       <userName>{username.text}</userName>  
  5.       <password>{password.text}</password>  
  6.      </mx:arguments>  
  7.     </mx:method>  
  8. </mx:RemoteObject>  

 

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

 

Xml代码
  1. if(Validator.validateAll(validators).length == 0){  
  2.   loginReq.login.send();  
  3. }    

 

 

 

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

 

 

Xml代码
  1. private function  loginHandler(event:ResultEvent):void{  
  2.   var isLogin:Boolean = event.result as Boolean;  
  3.  if(isLogin){  
  4.   this.parentApplication.viewStack.selectedIndex=1;  
  5.   dispatchEvent(new LoginUserEvent(username.text));  
  6.  }  
  7. }  

 

 

 

 

 

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

 

 

Xml代码
  1. dispatchEvent(new LoginUserEvent(username.text));  

 

 

 

 

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

 

 

Xml代码
  1.  <mx:Metadata>  
  2.      [Event(name="loginUser"type="flash.events.Event")]  
  3. </mx:Metadata  

 

 

 

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

 

 

Xml代码
  1. import flash.events.Event;  
  2.  public class LoginUserEvent extends Event  
  3.  {  
  4.   public static const LOGINUSEREVENT:String =”loginUser”;  
  5.   public var loginUserName:String = “”;  
  6.     
  7.   public function LoginUserEvent(userName:String)  
  8.   {  
  9.    super(LOGINUSEREVENT, true, true);  
  10.    this.loginUserName = userName;  
  11.   }  
  12.   override public function clone():Event {  
  13.        
  14.       return new LoginUserEvent(loginUserName);  
  15.        
  16.    }  

 

 

 

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

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

 

 

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

 

 

 

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

 

 

Xml代码
  1. private function getUserName(event:LoginUserEvent):void{  
  2. loginUserName = event.loginUserName;  
  3. loginReq.getAllFriends.send();  
  4. }  

 

 

 

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

 

Xml代码
  1.  private function getAllFriendsHandler(event:ResultEvent):void{  
  2.  mybuddyList = event.result as ArrayCollection;  
  3.    
  4. }  

 

 

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

 

Xml代码
  1. <mx:RemoteObject  id=”loginReq” destination=”flexmvcRO”>  
  2.     <mx:method name=”getAllFriends” result=”getAllFriendsHandler(event)” fault=”mx.controls.Alert.show(event.fault.faultString)”>  
  3.      <mx:arguments>  
  4.       <userName>{loginUserName}</userName>  
  5.      </mx:arguments>  
  6.     </mx:method>  
  7. </mx:RemoteObject>  

 

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

 

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

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

 

 

 

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

 

 

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

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

首先是LoginView.xml

Xml代码
  1. <?xml version=”1.0″ encoding=”utf-8″?>  
  2. <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”>  
  3. <mx:Script>  
  4.  <![CDATA[ 
  5.   import com.ny.flex.centralManagement.service.LoginService; 
  6.   import mx.validators.Validator; 
  7.   import mx.containers.ViewStack; 
  8.   import mx.rpc.events.ResultEvent; 
  9.   private function login():void{ 
  10.    if(Validator.validateAll(validators).length == 0){ 
  11.     LoginService.getInstance().login(username.text,password.text); 
  12.    }   
  13.   } 
  14.  ]]>  
  15. </mx:Script>  
  16.  <!–  Validators–>  
  17.  <mx:Array id=”validators”>  
  18.     <mx:StringValidator  id=”userNameValidator” source=”{username}”  property=”text”  required=”true”/>  
  19.     <mx:StringValidator  id=”passwordValidator” source=”{password}”  property=”text” required=”true” />  
  20.  </mx:Array>     
  21. <mx:Form id=”loginForm” x=”0″ y=”0″>  
  22.           <mx:FormItem label=”Username:” >  
  23.                <mx:TextInput id=”username” />  
  24.            </mx:FormItem>  
  25.            <mx:FormItem label=”Password:” >  
  26.                <mx:TextInput id=”password” displayAsPassword=”true” />  
  27.            </mx:FormItem>  
  28.            <mx:FormItem direction=”horizontal” verticalGap=”15″ paddingTop=”5″ width=”170″>  
  29.                <mx:Button id=”loginBtn” label=”Login” click=”login()”/>  
  30.            </mx:FormItem>  
  31.     </mx:Form>  
  32.       
  33. </mx:Panel>  

 

 

其功能的核心是:


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

 

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

LoginService类文件如下:

 

Xml代码
  1. import com.ny.flex.centralManagement.event.DataManagerResultEvent;  
  2.  import com.ny.flex.centralManagement.manager.GlobalObjectManager;  
  3.  import com.ny.flex.centralManagement.manager.RemoteObjectManager;  
  4.    
  5.  public class LoginService  
  6.  {  
  7.   public var roManager:RemoteObjectManager = null;   
  8.   public var gom:GlobalObjectManager = GlobalObjectManager.getInstance();  
  9.   private static var _instance:LoginService =null;  
  10.     
  11.   public static function getInstance():LoginService{  
  12.    if(_instance == null){  
  13.     _instance =  new LoginService(new PrivateClass)  
  14.    }  
  15.    return _instance;  
  16.      
  17.   }  
  18.   public function LoginService(privateclass:PrivateClass)  
  19.   {  
  20.    if(LoginService._instance == null){  
  21.     LoginService._instance =  this;  
  22.    }  
  23.   }  
  24.   public function login(userName:String,password:String):void{  
  25.  roManager = RemoteObjectManager.getRemoteObjectManager(”flexmvcRO”);  
  26.    roManager.addEventListener(”getLoginUser”,loginHandler);  
  27.    var params:Array = new Array(userName,password);  
  28.      
  29.    roManager.makeRemoteCall(”getLoginUserName”,”getLoginUser”,params);  
  30.   }  
  31.   private function loginHandler(event:DataManagerResultEvent):void {  
  32.    var userName:String = event.result as String;  
  33.    if(userName){  
  34.     gom.loginUserName = userName;  
  35.     gom.viewStackSelectedIndex=1;  
  36.    }  
  37.   }  
  38.  }  

 

 

 

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

            RemoteObjectManager

            GlobalObjectManager

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

Creating a Remote Object DataManager in ActionScript 3.0 for Flex 2.0

 

RemoteObjectManager.as:

Xml代码
  1. package com.ny.flex.centralManagement.manager  
  2. {  
  3.     import com.ny.flex.centralManagement.event.DataManagerResultEvent;  
  4.       
  5.     import flash.events.EventDispatcher;  
  6.       
  7.     import mx.core.Application;  
  8.     import mx.resources.ResourceManager;  
  9.     import mx.rpc.AbstractOperation;  
  10.     import mx.rpc.AsyncToken;  
  11.     import mx.rpc.events.FaultEvent;  
  12.     import mx.rpc.events.ResultEvent;  
  13.     import mx.rpc.remoting.mxml.RemoteObject;  
  14.       
  15.     public class RemoteObjectManager extends EventDispatcher {  
  16.         public var ro:RemoteObject;          
  17.         private var eventName:String;           
  18.           
  19.         private static var instanceMap:Object = new Object();      
  20.                
  21.         public function RemoteObjectManager(pri:PrivateClass,dest:String){              
  22.             this.ro = new RemoteObject();              
  23.             ro.destination = dest;  
  24.         }    
  25.                  
  26.         public static function getRemoteObjectManager(dest:String):RemoteObjectManager{  
  27.             if(RemoteObjectManager.instanceMap[dest] == null){                 
  28.                 RemoteObjectManager.instanceMap[dest] = new RemoteObjectManager(new PrivateClass(),dest);            
  29.             }            
  30.             var dm:RemoteObjectManager= RemoteObjectManager.instanceMap[dest];            
  31.             return dm;          
  32.         }  
  33.         public function makeRemoteCall(methodName:String,eventName:String,args:Array=null):void{  
  34.             this.eventName = eventName;              
  35.             var op:mx.rpc.AbstractOperation = ro[methodName];    
  36.             ro.addEventListener("result", doResults);              
  37.             ro.addEventListener("fault", doFault);      
  38.             var token:AsyncToken = null;          
  39.             if(args && args.length >0){                   
  40.                 token = op.send.apply(null,args);              
  41.             }    
  42.             else {  
  43.                 token = op.send();              
  44.             }   
  45.             token.eventName = eventName;         
  46.         }  
  47.         private function doResults(event:ResultEvent):void{               
  48.             var e:DataManagerResultEvent = new DataManagerResultEvent(event.token.eventName, event.result);   
  49.             this.dispatchEvent(e);          
  50.         }           
  51.         private function doFault(fault:FaultEvent):void{               
  52.             this.dispatchEvent(fault);           
  53.         }   
  54.              
  55.         public override function toString():String{               
  56.             return "RemoteObjectDataManager";           
  57.         }      
  58.      }  
  59. }  
  60.  /**   PrivateClass is used to make    DataManager constructor private  */     
  61.   class PrivateClass{  
  62.     public function PrivateClass() {}   
  63.   
  64.  }  

 

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

Xml代码
  1. <?xml version=”1.0″ encoding=”utf-8″?>  
  2. <mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml“  xmlns:views=”com.ny.flex.centralManagement.views.*” layout=”absolute”  width=”100%” height=”100%”>  
  3. <mx:Script>  
  4.  <![CDATA[ 
  5.   import mx.binding.utils.BindingUtils; 
  6.   import com.ny.flex.centralManagement.manager.GlobalObjectManager; 
  7.   [Bindable] 
  8.   public  var gom:GlobalObjectManager=GlobalObjectManager.getInstance(); 
  9.  ]]>  
  10. </mx:Script>  
  11.     <mx:HBox  horizontalAlign=”center” verticalAlign=”top”  width=”100%” height=”100%” y=”0″ x=”0″>  
  12.     <mx:ViewStack id=”viewStack”  resizeToContent=”true” selectedIndex=”{gom.viewStackSelectedIndex}” >  
  13.         <views:LoginView  />  
  14.         <views:BuddyListView/>  
  15.     </mx:ViewStack>  
  16.     </mx:HBox>  
  17. </mx:Application>  

 再回头看看在

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

LoginService

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

 

 

 

Xml代码
  1. private function loginHandler(event:DataManagerResultEvent):void {  
  2.   var userName:String = event.result as String;  
  3.   if(userName){  
  4.    gom.loginUserName = userName;  
  5.    gom.viewStackSelectedIndex=1;  
  6.   }  
  7.  }  

 

 

 

 

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

BuddyList.mxml代码:

Xml代码
  1. <?xml version=”1.0″ encoding=”utf-8″?>  
  2. <mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” title=”Buddy List of {gom.loginUserName}” creationComplete=”init()” width=”500″ height=”320″>  
  3. <mx:Script>  
  4.  <![CDATA[ 
  5.   import com.ny.flex.centralManagement.service.BuddyService; 
  6.   import com.ny.flex.centralManagement.manager.GlobalObjectManager; 
  7.   import mx.collections.ArrayCollection; 
  8.   import mx.rpc.events.ResultEvent; 
  9.   [Bindable] 
  10.   public var gom:GlobalObjectManager = GlobalObjectManager.getInstance(); 
  11.    
  12.   private function init():void{ 
  13.    BuddyService.getInstance().getBuddyList(); 
  14.   } 
  15.  ]]>  
  16. </mx:Script>  
  17.   
  18.  <mx:DataGrid id=”buddyList”  dataProvider=”{gom.mybuddyList}”  borderStyle=”none” width=”100%” height=”100%” >  
  19.        <mx:columns>  
  20.         <mx:DataGridColumn dataField=”firstName” headerText=”First Name”/>  
  21.         <mx:DataGridColumn dataField=”lastName” headerText=”Last Name”/>  
  22.     </mx:columns>  
  23.   
  24.  </mx:DataGrid>  
  25. </mx:Panel>  

 

 

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

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

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


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

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

 

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

 

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

 

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

 

Model Locater

Service Locator

Front Controller

 

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

 

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

 

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

 

看一下相关的代码:

 

BuddyAppModelLocator.as:

Xml代码
  1. <em>package com.ny.flex.cairngorm.model  
  2. {  
  3.       import com.ny.flex.cairngorm.vo.User;  
  4.         
  5.       import mx.collections.ArrayCollection;  
  6.         
  7.       [Bindable]  
  8.       public class BuddyAppModelLocator  
  9.       {  
  10.             public var buddyList:ArrayCollection=new ArrayCollection();  
  11.             public var loginUser:User=new User();  
  12.             public var viewStackSelectedIndex :int = 0;  
  13.               
  14.             static private var __instance:BuddyAppModelLocator=null;  
  15.               
  16.             static public function getInstance():BuddyAppModelLocator  
  17.             {  
  18.                   if(__instance == null)  
  19.                   {  
  20.                         __instance=new BuddyAppModelLocator();  
  21.                   }  
  22.                   return __instance;  
  23.             }  
  24.       }  
  25. }  
  26. </em>  

 

 

Model Locator代码中,定义了三个public的变量,buddyList:用来存放由数据库获取的密友列表;loginUser:定义一个User类型对象;viewStackSelectedIndex:定义viewStack指向的视窗。

几乎所有的服务层返回的信息都需要在Model Locator中有一个相应的对象。

 

 

BuddyServiceLocator.mxml:

Xml代码
  1. <em><em><?xml version=”1.0″ encoding=”utf-8″?>  
  2. <cairngorm:ServiceLocator xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:cairngorm=”http://www.adobe.com/2006/cairngorm“>  
  3.       <mx:RemoteObject id=”buddyRo“  destination=”flexmvcRO” >  
  4.               
  5.       </mx:RemoteObject>  
  6. </cairngorm:ServiceLocator>  
  7. </em></em>  

 

 

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

 

 

BuddyListController.as:

 

 

Xml代码
  1. <em><em><strong>package com.ny.flex.cairngorm.control  
  2. {  
  3.       import com.adobe.cairngorm.control.FrontController;  
  4.       import com.ny.flex.cairngorm.command.GetBuddyListCommand;  
  5.       import com.ny.flex.cairngorm.command.LoginCommand;  
  6.       import com.ny.flex.cairngorm.event.GetBuddyListEvent;  
  7.       import com.ny.flex.cairngorm.event.LoginEvent;  
  8.    
  9.       public class BuddyListController extends FrontController  
  10.       {  
  11.             public function BuddyListController()  
  12.             {  
  13.                   super();  
  14.                   addCommand(LoginEvent.LOGIN_EVENT,LoginCommand);        
  15.                   addCommand(GetBuddyListEvent.GET_BUDDY_LIST_EVENT,  
  16. GetBuddyListCommand);  
  17.             }  
  18.               
  19.       }  
  20. }  
  21. </strong></em></em>  

 

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

 

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

 

BuddList_Main_Cairngorm.mxml:

Xml代码
  1. <em><em><strong><?xml version=”1.0″ encoding=”utf-8″?>  
  2. <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%“>  
  3. <mx:Script>  
  4.       <![CDATA[ 
  5.             import com.ny.flex.cairngorm.model.BuddyAppModelLocator; 
  6.       [Bindable] 
  7.       public var myModel:BuddyAppModelLocator = BuddyAppModelLocator.getInstance(); 
  8.       ]]>  
  9. </mx:Script>  
  10.    
  11.       <service:BuddyServiceLocator id=”myservice“/>  
  12.       <controller:BuddyListController id=”myController“/>  
  13.    
  14.     <mx:HBox  horizontalAlign=”center” verticalAlign=”top“  width=”100%” height=”100%” y=”0” x=”0“>  
  15.     <mx:ViewStack id=”viewStack“  resizeToContent=”true” selectedIndex=”{myModel.viewStackSelectedIndex}” >  
  16.         <views:LoginView  />  
  17.         <views:BuddyListView/>  
  18.     </mx:ViewStack>  
  19.     </mx:HBox>  
  20. </mx:Application>  
  21. </strong></em></em>  

 

 

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

 

LoginView.mxml:

Xml代码
  1. <em><em><strong><em>      <![CDATA[ 
  2.             import com.ny.flex.cairngorm.event.LoginEvent; 
  3.             import com.ny.flex.cairngorm.vo.User; 
  4.             import mx.validators.Validator; 
  5.             private function login():void{ 
  6.                   if(Validator.validateAll(validators).length == 0){ 
  7.                         var loginUser:User = new User(); 
  8.                         loginUser.userName=username.text; 
  9.                         loginUser.password=password.text; 
  10.                         var loginEvent:LoginEvent = new LoginEvent(); 
  11.                         loginEvent.loginUser = loginUser; 
  12.                         loginEvent.dispatch(); 
  13.                   }    
  14.             } 
  15.       ]]>  
  16. </mx:Script>  
  17.    
  18.  <!–  Validators–>  
  19.  <mx:Array id=”validators“>  
  20.     <mx:StringValidator  id=”userNameValidator” source=”{username}“  property=”text“  required=”true“/>  
  21.     <mx:StringValidator  id=”passwordValidator” source=”{password}“  property=”text” required=”true” />  
  22.  </mx:Array>      
  23.    
  24.    
  25. <mx:Form id=”loginForm” x=”0” y=”0“>  
  26.           <mx:FormItem label=”Username:” >  
  27.                <mx:TextInput id=”username” />  
  28.            </mx:FormItem>  
  29.            <mx:FormItem label=”Password:” >  
  30.                <mx:TextInput id=”password” displayAsPassword=”true” />  
  31.            </mx:FormItem>  
  32.            <mx:FormItem direction=”horizontal” verticalGap=”15” paddingTop=”5” width=”170“>  
  33.                <mx:Button id=”loginBtn” label=”Login” click=”login()”/>  
  34.            </mx:FormItem>  
  35.     </mx:Form>  
  36.         
  37. </mx:Panel>  
  38. </em></strong></em></em>  

 

 

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

 

 

 

LoginEvent.as:

Xml代码
  1. <em><em><strong><em>package com.ny.flex.cairngorm.event  
  2. {  
  3.       import com.adobe.cairngorm.control.CairngormEvent;  
  4.       import com.ny.flex.cairngorm.vo.User;  
  5.         
  6.       import flash.events.Event;  
  7.    
  8.       public class LoginEvent extends CairngormEvent  
  9.       {  
  10.             public static var LOGIN_EVENT:String = “loginEvent”  
  11.             public  var  loginUser:User ;  
  12.               
  13.             public function LoginEvent()  
  14.             {  
  15.                   super(LOGIN_EVENT);  
  16.             }  
  17.    
  18.             override public function clone() : Event  
  19.             {  
  20.                   return new LoginEvent();  
  21.             }  
  22.       }  
  23. }  
  24. </em></strong></em></em>  

 

 

每一个事件都 要对应于一个命令:

LoginCommand.as:

Xml代码
  1. <em><em><strong>package com.ny.flex.cairngorm.command  
  2. {  
  3.       import com.adobe.cairngorm.commands.ICommand;  
  4.       import com.adobe.cairngorm.control.CairngormEvent;  
  5.       import com.ny.flex.cairngorm.event.LoginEvent;  
  6.       import com.ny.flex.cairngorm.model.BuddyAppModelLocator;  
  7.       import com.ny.flex.cairngorm.service.LoginDelegate;  
  8.       import com.ny.flex.cairngorm.vo.User;  
  9.         
  10.       import mx.controls.Alert;  
  11.       import mx.rpc.IResponder;  
  12.    
  13.       public class LoginCommand implements ICommand, IResponder  
  14.       {  
  15.             public function LoginCommand()  
  16.             {  
  17.             }  
  18.    
  19.             public function execute(event:CairngormEvent):void  
  20.             {  
  21.                   var loginEvent:LoginEvent = LoginEvent(event);  
  22.                   var user:User = loginEvent.loginUser;  
  23.                   var lgoinService :LoginDelegate   
  24. =  new LoginDelegate(this);  
  25.                   lgoinService.authenticate(user);  
  26.             }  
  27.               
  28.             public function result(event:Object):void  
  29.             {  
  30.                   var authUser:User = User(event.result);  
  31.                   BuddyAppModelLocator.getInstance().loginUser = authUser;  
  32.                   BuddyAppModelLocator.getInstance().viewStackSelectedIndex=1;  
  33.             }  
  34.               
  35.             public function fault(info:Object):void  
  36.             {  
  37.                   Alert.show(“Login Fail Error “);  
  38.             }  
  39.               
  40.       }  
  41. }  
  42. </strong></em></em>  

 

 

 

 

 

 

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

 

 addCommand(LoginEvent.LOGIN_EVENT,LoginCommand);

 

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

 

Xml代码
  1. <em><em><strong>                  var lgoinService :LoginDelegate =    
  2.                                             new LoginDelegate(this);  
  3.                   lgoinService.authenticate(user);  
  4. </strong></em></em>  

 

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

 

LoginDelegate.as:

Xml代码
  1. <em><em><strong><em>package com.ny.flex.cairngorm.service  
  2. {  
  3.       import com.adobe.cairngorm.business.ServiceLocator;  
  4.       import com.ny.flex.cairngorm.vo.User;  
  5.         
  6.       import mx.rpc.IResponder;  
  7.         
  8.       public class LoginDelegate  
  9.       {  
  10.             private var responder:IResponder;  
  11.             private var service:Object;  
  12.               
  13.             public function LoginDelegate(responder :IResponder){  
  14.               this.service =   
  15.                            ServiceLocator.getInstance()  
  16.                                         .getRemoteObject(“buddyRo”);  
  17.               this.responder = responder;  
  18.             }  
  19.               
  20.             public function  authenticate(user:User):void{  
  21.                   var call:Object = service.authenticate(user);  
  22.                   call.addResponder(responder);  
  23.             }  
  24.       }  
  25. }  
  26.    
  27. </em></strong></em></em>  

 

 

 

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

LoginCommand.as:

Xml代码
  1. <em><em><strong>            public function result(event:Object):void  
  2.             {  
  3.                   var authUser:User = User(event.result);  
  4.                   BuddyAppModelLocator.getInstance().loginUser  
  5.                                                                                     = authUser;  
  6.                         BuddyAppModelLocator.getInstance().viewStackSelectedIndex=1;  
  7.             }  
  8. </strong></em></em>  

 

 

 

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

 

使用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代码
  1. package com.ny.flex.cairngorm.no_fc.service  
  2. {  
  3.     import com.ny.flex.cairngorm.no_fc.vo.User;  
  4.       
  5.     public class ServiceFacade  
  6.     {  
  7.         private static var _serviceFacade:ServiceFacade = null;  
  8.           
  9.         public function ServiceFacade(privateClass:PrivateClass)  
  10.         {  
  11.             if(ServiceFacade._serviceFacade == null){  
  12.                 ServiceFacade._serviceFacade = this;  
  13.             }  
  14.         }  
  15.         public static function getInstance():ServiceFacade {  
  16.             if(_serviceFacade == null){  
  17.                 _serviceFacade = new ServiceFacade(new PrivateClass);  
  18.             }  
  19.             return _serviceFacade;  
  20.         }  
  21.           
  22.         public function authenticate(user:User):void{  
  23.             LoginDelegate.getInstance().authenticate(user);  
  24.         }  
  25.         public function  getBuddyList():void{  
  26.             BuddyListDelegate.getInstance().getBuddyList();  
  27.         }  
  28.     }  
  29. }  
  30.   
  31. class  PrivateClass{}  

 

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

Loginvew.xml :

 

 

Xml代码
  1. private function login():void{  
  2.         if(Validator.validateAll(validators).length == 0){  
  3.             var loginUser:User = new User();  
  4.             loginUser.userName=username.text;  
  5.             loginUser.password=password.text;  
  6.             <span style="color: rgb(255, 102, 0);"><strong><em>serviceFacade.authenticate(loginUser);</em></strong></span>  
  7.         }     
  8.     }  

 

 

函数 serviceFacade.authenticate(loginUser)如下:

 

 

Xml代码
  1. <strong>        public function authenticate(user:User):void{  
  2.             <span style="color: rgb(255, 102, 0);"><strong><em>LoginDelegate.</em></strong></span>getInstance().authenticate(user);  
  3.         }  
  4. </strong>  

  

ServiceFacade使用LoginDelegate 来真正实现Business Logic:

 

 

 

 

 

 LoginDelegate.as:

 

 

Xml代码
  1. <strong>package com.ny.flex.cairngorm.no_fc.service  
  2. {  
  3.     import com.ny.flex.cairngorm.no_fc.*;  
  4.     import com.ny.flex.cairngorm.no_fc.vo.User;  
  5.       
  6.     import mx.rpc.IResponder;  
  7.     import mx.rpc.Responder;  
  8.     import mx.rpc.events.ResultEvent;  
  9.       
  10.     public class LoginDelegate extends BaseDelegate   
  11.     {  
  12.         private static  var _loginDelegate:LoginDelegate = null;  
  13.           
  14.         public function LoginDelegate(privateClass:PrivateClass){  
  15.             if(LoginDelegate._loginDelegate == null ){  
  16.                 LoginDelegate._loginDelegate = this;  
  17.             }  
  18.           
  19.         }  
  20.           
  21.         public  static function  getInstance():LoginDelegate{  
  22.             if(_loginDelegate == null){  
  23.                 _loginDelegate = new LoginDelegate(new PrivateClass);  
  24.             }  
  25.             return  _loginDelegate;  
  26.         }  
  27.           
  28.         public function <span style="color: rgb(255, 102, 0);"><strong><em> authenticate</em></strong></span>(user:User):void{  
  29.             var responder:IResponder = new Responder(onResult_Authenticate,fault);  
  30.             var call:Object = service.authenticate(user);  
  31.             call.addResponder(responder);  
  32.         }  
  33.           
  34.         private function onResult_Authenticate(event:ResultEvent):void{  
  35.             var authUser:User = event.result as User;  
  36.             model.loginUser = authUser;  
  37.             model.viewStackSelectedIndex = 1;  
  38.         }  
  39.       
  40.     }  
  41. }  
  42.   
  43. class PrivateClass{}</strong>  

  

 

 

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

 

 

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

 

 

 

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

 

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

 

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

 

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

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

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

BuddyListEventMap.mxml:

 

Xml代码
  1. <?xml version=”1.0″ encoding=”utf-8″?>  
  2.     
  3. <EventMap xmlns=”http://mate.asfusion.com/”   
  4.                              xmlns:mx=http://www.adobe.com/2006/mxml>  
  5.   
  6. </EventMap>  

 

 

 

 

 

 

 

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

主程序 Flex_Mate.mxml:

Xml代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <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">  
  3. <mx:Script>  
  4.     <![CDATA[ 
  5.         [Bindable] 
  6.         public var viewStackSelectedIndex :int = 0; 
  7.     ]]>  
  8. </mx:Script>  
  9. <span style="color: rgb(255, 102, 0);"><strong><em><map:BuddyListEventMap/></em></strong></span>  
  10.     <mx:HBox  horizontalAlign="center" verticalAlign="top"  width="100%" height="100%" y="0" x="0">  
  11.     <mx:ViewStack id="viewStack"  resizeToContent="true" selectedIndex="{viewStackSelectedIndex}" >  
  12.         <views:LoginView  />  
  13.         <views:BuddyListView/>  
  14.     </mx:ViewStack>  
  15.     </mx:HBox>  
  16. </mx:Application>  

 

 

 

 

 

 

2. 建立LoginView :

Xml代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="300" height="200" horizontalAlign="center" verticalAlign="middle" title="Flex Cirngorm Login">  
  3.  <mx:Script>  
  4.     <![CDATA[ 
  5.         import com.ny.flex.mate.event.LoginEvent; 
  6.         import com.ny.flex.mate.vo.User; 
  7.         import mx.validators.Validator; 
  8.         private function login():void{ 
  9.             if(Validator.validateAll(validators).length == 0){ 
  10.                 var loginUser:User = new User(); 
  11.                 loginUser.userName=username.text; 
  12.                 loginUser.password=password.text; 
  13.                 <span style="color: rgb(255, 102, 0);"><strong><em>var loginEvent:LoginEvent = new LoginEvent(LoginEvent.LOGIN); 
  14.                 loginEvent.loginUser = loginUser; 
  15.                 dispatchEvent(loginEvent);</em></strong></span> 
  16.             }    
  17.         } 
  18.     ]]>  
  19. </mx:Script>  
  20.   
  21.  <!--  Validators-->  
  22.  <mx:Array id="validators">  
  23.     <mx:StringValidator  id="userNameValidator" source="{username}"  property="text"  required="true"/>  
  24.     <mx:StringValidator  id="passwordValidator" source="{password}"  property="text" required="true" />  
  25.  </mx:Array>      
  26.   
  27.   
  28. <mx:Form id="loginForm" x="0" y="0">  
  29.           <mx:FormItem label="Username:" >  
  30.                <mx:TextInput id="username" />  
  31.            </mx:FormItem>  
  32.            <mx:FormItem label="Password:" >  
  33.                <mx:TextInput id="password" displayAsPassword="true" />  
  34.            </mx:FormItem>  
  35.            <mx:FormItem direction="horizontal" verticalGap="15" paddingTop="5" width="170">  
  36.                <mx:Button id="loginBtn" label="Login" click="login()"/>  
  37.            </mx:FormItem>  
  38.     </mx:Form>  
  39.       
  40. </mx:Panel>  

 

 

 

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

Xml代码
  1. package com.ny.flex.mate.event  
  2. {  
  3.     import com.ny.flex.mate.vo.User;  
  4.       
  5.     import flash.events.Event;  
  6.   
  7.     public class LoginEvent extends Event  
  8.     {  
  9.         public static const LOGIN:String = "login";  
  10.         public var  loginUser:User;  
  11.         public function LoginEvent(type:String, <span style="color: rgb(255, 102, 0);"><strong><em>bubbles:Boolean=true</em></strong></span>cancelable:Boolean=false)  
  12.         {  
  13.             super(type, bubbles, cancelable);  
  14.         }  
  15.           
  16.     }  
  17. }  

 

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

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

Xml代码
  1. …..  
  2.    
  3. <EventHandlers type=”{LoginEvent.LOGIN}“>  
  4.      <RemoteObjectInvoker destination=”flexmvcRO”   
  5.                                                 method=”authenticate”   
  6.                                                 arguments=”{event.loginUser}“>  
  7.    
  8.          <resultHandlers>  
  9.                <MethodInvoker generator=”{<span style="color: rgb(255, 102, 0);"><strong><em>LoginService</em></strong></span>}”   
  10.                                          method=”<span style="color: rgb(255, 0, 0);"><strong><em>onResult_Authenticate</em></strong></span>”   
  11.                                                     arguments=”{<span style="color: rgb(128, 0, 0);"><strong><em>resultObject</em></strong></span>}“/>  
  12.         </resultHandlers>  
  13.     </RemoteObjectInvoker>  
  14.  </EventHandlers>  
  15. ……  

 

 

 

 

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

来看看 LoginService.as代码::

 

 

 

Xml代码
  1. package com.ny.flex.mate.service  
  2. {  
  3.     import com.ny.flex.mate.vo.User;  
  4.       
  5.     public class <span style="color: rgb(255, 102, 0);"><strong><em>LoginService</em></strong></span>  
  6.     {  
  7.         [Bindable]  
  8.         public var authUserName:String;  
  9.         [Bindable]  
  10.         public var viewStackSelectedIndex:int ;  
  11.   
  12.         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{  
  13.             authUserName = user.userName;  
  14.             viewStackSelectedIndex = 1;  
  15.         }  
  16.           
  17.     }  
  18. }  

 

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

 

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

 

 

Xml代码
  1. <Injectors target=”{BuddyListView}“>  
  2.            <PropertyInjector targetKey=”authUserName”       
  3.                                         source=”{LoginService}”   
  4.                                         sourceKey=”authUserName“/>  
  5.    
  6.  </Injectors>  
  7.    
  8. <Injectors target=”{Flex_Mate}“>  
  9.               <PropertyInjector targetKey=”viewStackSelectedIndex”   
  10.                                source=”{LoginService}”  
  11.                                sourceKey=”viewStackSelectedIndex“/>  
  12.    
  13.  </Injectors>  
  14.    
  15.    

 

 

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

 

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

Xml代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" title="Buddy List of  {authUserName}" creationComplete="getBuddyList()" width="500" height="320">  
  3. <mx:Script>  
  4.     <![CDATA[ 
  5.         import mx.collections.ArrayCollection; 
  6.         import com.ny.flex.mate.event.GetBuddyListEvent; 
  7.        [Bindable] 
  8.        public var authUserName:String; 
  9.        [Bindable] 
  10.        public var buddyCollection:ArrayCollection; 
  11.         
  12.        private function getBuddyList():void{ 
  13.             var getBuddyListEvent:GetBuddyListEvent = new GetBuddyListEvent(GetBuddyListEvent.GET_BUDDY_LIST); 
  14.             getBuddyListEvent.authUserName = authUserName; 
  15.             dispatchEvent(getBuddyListEvent); 
  16.         } 
  17.     ]]>  
  18. </mx:Script>  
  19.   
  20.  <mx:DataGrid id="buddyList"  dataProvider="{buddyCollection}"  borderStyle="none" width="100%" height="100%" >  
  21.        <mx:columns>  
  22.         <mx:DataGridColumn dataField="firstName" headerText="First Name"/>  
  23.         <mx:DataGridColumn dataField="lastName" headerText="Last Name"/>  
  24.     </mx:columns>  
  25.   
  26.  </mx:DataGrid>  
  27. </mx:Panel>  

 

 

 

 

 

 

整个开发流程是这样的:

 

 

 

 

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

 

整个项目结构图 见附件

 

 

 

总结:

 

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

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

 

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

对于Cairngorm

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

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).

 

我在期待着它的来临。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值