zoukankan      html  css  js  c++  java
  • 基于 Cairngorm MVC 框架的 Flex 程序设计与开发

    Cairngorm 框架简介

    Cairngorm 是一种 MVC 框架,它可以帮助开发者很好的组织代码,分层,从而使得代码更加清晰易懂,可以使设计者,UI 组件开发者,数据服务开发者并行工作,从而提高开发效率。Cairngorm 由五大组件构成,分别作为存放数据的仓库 (ModelLocator),存放服务的仓库 (ServiceLocator),处理业务逻辑的命令 (Command),自定义的用于触发命令执行的事件 (Event),用于把事件映射到命令的前端控制器 (FrontController)。它应用了代理模式,命令模式,观察者和单例等几种设计模式。


    Cairngorm 详解

    ModelLocator: 用于保存应用程序的数据,与 HttpSession 类似,区别主要在于数据是保存客户端,实现时将 ModelLocator 实现为单例,所有的应用数据都存储在唯一的一个 ModelLocator 对象中。

    CairngormEvent: 这个类由 Cairngorm 框架提供,位于包 com.adobe.cairngorm.control,所有的自定义的事件类都必须继承 CairngormEvent,在自定义事件类中定义 VO 的引用,可以通过它来存储从 View 层传递过来的 VO。

    ICommand: 命令接口,位于包 com.adobe.cairngorm.commands,每个命令类需要实现 ICommand 接口,命令类用于处理业务逻辑,通过实现其 execute 方法来实现业务逻辑。execute 方法以事件作为参数,通过传入的事件中包含的详细信息来进行相应的处理。

    FrontController: 前端控制器,位于包 com.adobe.cairngorm.control,自定义的前端控制器必须继承 FrontController,它主要用于完成 CairngormEvent 和 ICommand 之间的映射。

    CairngormEventDispatcher:CairngormEvent 的分发器,位于包 com.adobe.cairngorm.control

    Delegate: 服务代理,一般定义一个 Delegate 类,持有 ServiceLocator 的引用,Delegate 类是唯一的了解 Service 有关的类。


    系统处理流程


    图 1. 使用 Cairngorm 后系统处理流程
    图 1. 使用 Cairngorm 后系统处理流程

    在基于 Cairngorm 的应用程序中,应用程序的数据都放在一个仓库中,这个仓库就是 ModelLocator,整个应用程序只生成维护一个 ModelLocator 的实例,在需要数据的组件中引用这个实例。比如视图需要数据进行渲染,那么视图就维护 ModelLocator 实例的引用,在视图中触发事件后,事件通过前端控制器映射为一个对应的命令,然后命令通过调用服务代理,执行相应的逻辑,命令通过更新自身维护的 ModelLocator 的引用,因为 ModelLocator 只有一个,这样就相当于更新了视图。


    安装配置开发环境

    开始前先让我们来搭建我们的开发环境:

    下载并安装JDK( 本文使用版本为 Sun JDK 6)

    下载并解压Eclipse( 本文使用版本为 Eclipse Ganymede J2EE SR2 版本 )

    下载并安装FB3_WWEJ_Plugin.exe(Flex Builder 4 plug-in for eclipse)

    下载并安装 Tomcat 6.x

    安装 FB3_WWEJ_Plugin.exe 过程中选中 eclipse 安装目录。


    一个入门的例子

    前面我们讲解了 Cairngorm 的基本概念和一些主要的角色和类,下面我用一个简单的员工管理系统的例子来进一步的理解开发过程。

    解压出来就是一个 Cairngorm.swc 文件

    • 新建 Flex Project

    图 2. 新建项目
    图 2. 新建项目

    Next 配置服务器


    图 3. 配置服务器
    图 3. 配置服务器

    点击 Finish.

    将 Cairngorm.swc 拷贝到项目的 flex_libs 目录下。

    在 flex_src 目录下建好包结构


    图 4. 建立包结构
    图 4. 建立包结构

    每个包中存放的内容一目了然

    • 定义视图
    1. 在 views 包中新建一个 MXML Component, 基于 Panel,取名为 PeopleInfo.mxml,如图所示 :

      图 5. 新建 Panel
      图 5. 新建 Panel

    2. 在里面添加一个 DataGrid, 还有一些文本框和按钮 , 如图 6 所示:

      图 6. 视图
      图 6. 视图

    三、这里我们希望当 Panel 加载完成后,从服务器读取 xml 文件,将员工信息给读入 DataGrid 中,这里,为了简单明了,仅仅定义了员工 id 和 name 属性。所以在 Panel 中添加属性 creationComplete="LoadPersons();",然后添加<mx:Script>标签,并添加LoadPersons()方法,原型如下所示:


    清单 1. 原型
    				
    public
    functionLoadPersons():void
     { 
     } 
    

    • 定义 ModelLocator

    视图中的 DataGrid 是需要数据进行渲染的,下面定义数据的仓库 ModelLocator,该类维护一个 ArrayCollection personInfos 的引用,它就是应用程序所需的数据。

    新建一个 Actionscript class,取名为 ModelLocator.as,代码如下:


    清单 2. ModelLocator.as
    				
     package model 
     { 
    		 import mx.collections.ArrayCollection; 
    		 [Bindable] 
    		 public class ModelLocator 
    		 { 
    			 private static var __instance:ModelLocator=null; 
     //以下这个 ArrayCollection用于填充 DataGrid
           public var personInfos:ArrayCollection = new ArrayCollection();
    			 public static  function getInstance():ModelLocator 
    			 { 
    				 if(__instance == null) 
    				 { 
    					 __instance=new ModelLocator(); 
    				 } 
     return __instance;
    			 } 
    		 } 
     } 
    

    ArrayCollection 对象 personInfos 用于存储从 xml 读出的员工信息。

    • 定义事件

    在 event 包中新建一个 ActionScript class,取名为 LoadPersonsEvent.as,该事件必须继承于 CairngormEvent,通过 CairngormEventDispatcher 来分发 , EVENT_ID用来唯一的标识一个事件,代码如下:


    清单 3. LoadPersonsEvent.as
    				
     package event 
     { 
    		 import com.adobe.cairngorm.control.CairngormEvent; 
    
     public class LoadPersonsEvent extends CairngormEvent
    		 { 
     //事件名称
    			 public static var EVENT_ID:String = "LoadPersons"; 
    			 public function LoadPersonsEvent() 
    			 { 
    				 super(EVENT_ID); 
    			 } 
    		 } 
     } 
    

    • 在页面中注册并分发事件

    在 PeopleInfo.mxml 的 <mx:Panel> 下面添加如下代码:


    清单 4.
    				
     <mx:Metadata> 
    		 [Event(name="LoadPersons",type="event.LoadPersonsEvent")] 
     </mx:Metadata> 
    

    type 属性就是类的完整路径。

    在 LoadPersons() 方法中添加如下代码:


    清单 5.
    				
     var e:LoadPersonsEvent = new LoadPersonsEvent(); 
     e.dispatch(); 
    

    当事件分发后,通过 FrontController 将事件映射为命令,然后命令更新 ModelLocator 中的数据的时候,从而引起 DataGrid 的变化,从中可以看出,需要在页面中维护一个 ModelLocator 单例的引用 , 代码如下:

    然后在 <mx:Script> 中添加以下代码:


    清单 6.
    				
     [Bindable] 
     public var _model:ModelLocator = ModelLocator.getInstance(); 
    

    然后将 _model.personInfos 作为 DataGrid 的 dataProvider, 为 <mx:DataGrid> 添加如下属性:


    清单 7.
    				
    dataProvider="{_model.personInfos}"
    			

    • 定义 FrontController

    事件分发后,应该执行一个对应的命令,命令通过执行相应的业务逻辑,更新数据,从而视图得到更新,那到底事件和命令之间是如何映射的呢?

    在包 control 中新建一个 ActionScript class,取名为 MyFrontControl.as,代码如下:


    清单 8. MyFrontControl.as
    				
     package control 
     { 
    		 import com.adobe.cairngorm.control.FrontController; 
     import event.*;
    		 import commands.*; 
    		 public class MyFrontControl extends FrontController 
    		 { 
    			 public function MyFrontControl() 
    			 { 
    				 super(); 
    			
     //注册一个事件,将其与一个命令绑定
    			
     this.addCommand(LoadPersonsEvent.EVENT_ID,LoadPersonCommand);
    			 } 
    		
    		 } 
     } 
    

    这里的 addCommand() 方法将事件和命令关联起来 , 第一个参数是事件的 name,第二个参数是具体的命令类,这里 LoadPersonCommand 还没有定义,编译错,下面就来定义 LoadPersonCommand.

    • 定义 Command

    新建一个 ActionScript class,取名为 LoadPersonCommand.as,实现 ICommand 接口,

    命令调用服务代理完成相应的逻辑,然后通过更新数据,间接的反映到视图上,所以命令中需要维护 ModelLocator 单例的引用,每个命令类的入口函数是 execute() 方法。

    代码如下:


    清单 9. LoadPersonCommand.as
    				
     package commands 
     { 
    		 import com.adobe.cairngorm.commands.ICommand; 
    		 import com.adobe.cairngorm.control.CairngormEvent; 
    		 import mx.collections.ArrayCollection; 
    		 import mx.controls.Alert; 
    		 import mx.rpc.Responder; 
     import mx.rpc.events.FaultEvent;
    		 import mx.rpc.events.ResultEvent; 
    		 public class LoadPersonCommand implements ICommand 
    		 { 
    			 public function execute(event:CairngormEvent):void 
    			 { 
    
    			 } 
    		
     //如果请求发送成功则执行这个方法
     public function onResults_loadPersons(event:ResultEvent):void
    			 { 
    				
    			 } 
     //如果请求失败则执行这个方法
    			 public function onFaults_loadPersons(event:FaultEvent):void 
    			 { 
    				
    			 } 
    		
    		 } 
     } 
    

    execute() 方法具体怎么实现,需要依赖于 service, 下面就来定义 ServiceLocator

    • 定义 ServiceLocator

    在 business 包下新建一个 MXML Component,取名为 ServiceLocator.mxml,由于插件不支持 com.adobe.cairngorm.business.ServiceLocator,所以 Based on 暂时随便选择,等会手工修改,修改后的代码如下:


    清单 10. ServiceLocator.mxml
    				
     <?xml version="1.0" encoding="utf-8"?> 
     <rds:ServiceLocator xmlns:rds="com.adobe.cairngorm.business.*"
     xmlns:mx="http://www.adobe.com/2006/mxml">
         <! —服务 -->
         <mx:HTTPService  id="getPersonInfos" url="/xml/abc.xml"/>
     </rds:ServiceLocator> 
    

    这里定义了一个 HTTPService 服务,获取服务器下的 xml 目录下的 abc.xml 的内容,在 WebContent 目录下建立一个目录,取名为 xml,并且在其下建立一个 abc.xml,内容如下:


    清单 11. abc.xml
    				
     <menus> 
     <Box> 
       <id>1</id> 
     <name>zhangsan</name>
     </Box> 
     <Box> 
    	<id>2</id> 
    	<name>lisi</name> 
     </Box> 
     <Box> 
    	<id>3</id> 
    	<name>wangwu</name> 
     </Box> 
     </menus> 
    

    前面提到,通过定义 delegate 类来对服务进行管理易于维护,以下定义 delegate 类

    • 定义 Delegate 类

    在 business 包下新建一个 delegates 包,在其下建立一个 ActionScript class 类,取名为 PersonDelegate.as,该方法通过引用 ServiceLocator,获取并调用其中的服务,代码如下:


    清单 12. PersonDelegate.as
    				
     package business.delegates 
     { 
    		 import com.adobe.cairngorm.business.ServiceLocator; 
    		 import mx.rpc.http.HTTPService; 
    		 import mx.rpc.IResponder; 
    		 import mx.rpc.AsyncToken; 
    		 import mx.controls.Alert; 
    		 public class PersonDelegate 
    		 { 
             public var serviceLocator:ServiceLocator = ServiceLocator.getInstance();
    			
    			 public var _service:HTTPService; 
    			
    			 public var _responder:IResponder; 
    			
    			 public function PersonDelegate(responder:IResponder) 
    			 { 
     //getHTTPService的参数对应 Services.mxml中的 id
            _service = serviceLocator.getHTTPService("getPersonInfos"); 
    				 _responder = responder; 
    			 } 
    			
    			 public function getPersonInfos():void 
    			 { 
     //发送请求
    				 var token:AsyncToken = _service.send(); 
    				 token.addResponder(_responder); 
    			 } 
    
    		 } 
     } 
    

    方法 getPersonInfos() 用于发送查询 abc.xml 文件的请求。

    • 定义 VO

    从 xml 文件中读取过来的数据实际上都是一个个的员工的信息,便于存储,定义 VO 类来封装员工信息,在 vo 包中定义 ActionScript class,取名为 PersonForm.as,代码如下:


    清单 13. PersonForm.as
    				
     package vo 
     { 
    		 public class PersonForm 
    		 { 
    			 public var id:String=""; 
    			 public var name:String=""; 
    			 public function PersonForm() 
    			 { 
    			
    			
    			 } 
    		 } 
     } 
    

    • 实现 Command

    通过在 Command 中维护 ModelLocator 单例的引用,当更新 ModelLocator 中的数据时,因为 ModelLocator 是单例,所以页面中的 ModelLocator 中的数据也相应的更新,详细的原理请看注释,LoadPersonCommand.as 完整代码如下:


    清单 14. LoadPersonCommand.as
    				
     package commands 
     { 
    import business.delegates.PersonDelegate; 
    import com.adobe.cairngorm.commands.ICommand; 
    import com.adobe.cairngorm.control.CairngormEvent; 
    import model.ModelLocator; 
    import mx.collections.ArrayCollection; 
    import mx.controls.Alert; 
    import mx.rpc.Responder; 
    import mx.rpc.events.FaultEvent; 
    import mx.rpc.events.ResultEvent; 
    import vo.PersonForm; 
    public class LoadPersonCommand implements ICommand 
     {
            private var _model:ModelLocator=ModelLocator.getInstance();
    	public function execute(event:CairngormEvent):void
    	{
    	//注册两个回调函数
    var responder:Responder = 
    new Responder(onResults_loadPersons, onFaults_loadPersons);
    var delegate:PersonDelegate = new PersonDelegate(responder);
    	delegate.getPersonInfos();
    	}
    	//如果请求发送成功则执行
     public function onResults_loadPersons(event:ResultEvent):void
    	{
    	//用 xml 文件中的数据填充 ArrayCollection
    	var personRaw:ArrayCollection =event.result.menus.Box;
    	//personInfo填充 Model
    	for(var i:int=0;i<personRaw.length;i++)
    	{
    	var tempPerson:PersonForm=new PersonForm();
    	var personFromXML:Object=personRaw.getItemAt(i);
    	tempPerson.id=personFromXML.id;
    	tempPerson.name=personFromXML.name;
    	_model.personInfos.addItem(tempPerson);
    	}
    	}
    	//如果请求发送不成功则执行
     public function onFaults_loadPersons(event:FaultEvent):void
    	{
    	Alert.show("failed");
    	}
    	}
     } 
    

    execute() 方法中 PersonDelegate 执行 getPersonInfos() 方法,当请求发送成功,返回数据的时候,通过注册的回调函数 onResults_loadPersons(event:ResultEvent) 接收返回的数据,将每条员工信息用 VO 封装起来,然后更新 ModelLocator 中的数据,从而页面上的数据得到更新。

    • 创建 MXML Application

    以上将所有的组件已经创建完毕,现在是时候创建一个 Application 将它们组装起来。

    在 flex_src 下新建一个 MXML Application,取名为 Cairngormdemo.mxml,代码如下:


    清单 15. Cairngormdemo.mxml
    				
     <?xml version="1.0" encoding="utf-8"?> 
     <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" 
    		 xmlns:views="views.*"
    		 xmlns:rds="business.*"
    		 xmlns:control="control.*"> 
    	 <mx:Style source="assets/style.css"/> 
    	
    		 <views:PeopleInfo  x="51"/> 
    		 <rds:Services /> 
    		 <control:MyFrontControl/> 
    	
    	
     </mx:Application> 
    

    在页面中将自定义组件 PeopleInfo,ServiceLocator 和 FrontController 实例化。

    • 运行

    运行 Cairngormdemo.mxml,如下图所示 :


    图 7. 结果
    图 7. 结果

    这里仅仅实现了加载数据的功能,增删改留给读者自己实现。


    结束语

    本文对 Flex MVC 框架 Cairngorm 的重要部分作了一个详细的介绍,当然还有一些细节问题值得研究,相信通过本文的示例项目,可以帮助您更快的掌握这种全新的开发方式。



    下载

    描述 名字 大小 下载方法
    演示代码 Cairngormdemo.zip 699KB HTTP

    关于下载方法的信息


    参考资料

    学习

    讨论

    作者简介

    翟峰在 J2EE 和 Web 开发领域有着多年开发经验,喜欢关注新技术。毕业于西北大学,拥有计算机硕士学位。

    吴镝主要关注 J2EE 和 Web 开发领域,喜欢关注和学习新技术,目前就读于天津大学,攻读计算机科学与技术硕士学位。

  • 相关阅读:
    python---模块与包
    python---迭代器与生成器
    python---装饰器
    Python---函数
    Python---文件操作
    Python---数据类型
    浅谈UBUNTU
    java 键盘输入多种方法
    动态规划解最长公共子序列问题
    线段树
  • 原文地址:https://www.cnblogs.com/bmate/p/1879936.html
Copyright © 2011-2022 走看看