代码改变世界

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

2008-10-13 23:42  宝宝合凤凰  阅读(502)  评论(0)    收藏  举报

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

 http://houwei.javaeye.com/blog/219666

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>  
<?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代码 复制代码
  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.  }  
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代码 复制代码
  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.  }  
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之间传递信息,例如,我们使用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>  
<?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代码 复制代码
  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.  }  
 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代码 复制代码
  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>  
<?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