第十一章 深入理解Cairngorm架构模式

来源:互联网 发布:淘宝卖家如何看加购 编辑:程序博客网 时间:2024/04/29 18:18
 

                        第十一章  深入理解Cairngorm架构模式

任何Flex应用资源包都是MXMl文件、ActionScript文件,基础类库swc文件以及一系列环境配置文件的混合体。如果没有框架的支持,各个模块的组织看起来总是乱糟糟的一片,尤其当你的应用规模变得原来越大的时候。如题所示,Cairngorm框架模式在一定程度上解决了这个问题。它使客户端的开发变得有条理起来,开发者可以根据需要把整个客户端逻辑规整为一个局部MVC(Module-View-Controller)架构模式上来。从设计模式上讲,MVC不是一个设计模式,而是一个更高层次上的架构组织模式。也就是说集成了Cairngorm架构的应用,客户端逻辑的各个模块看起来会更加清晰和紧凑一些,同时也避免了各个模块间紧密的耦合,有利于系统将来的维护和扩展。Cairngorm说到底只为开发者提供了一种Flex应用的参考思维方式,这个方式就是MVC。在它的模型、视图、控制的三层结构中,都提供了很好的支持。Flex应用在Cairngorm框架的支持下,总是可被分成这三个层次。Flex的开发也就变成了对这三层的逻辑的实现。本章将结合Cairngorm官方的一个实例详细讲解它是什么、怎么用的细节。另外也将细细分析以实际的项目尤其是大型项目的眼光来看,这个官方示例有待改进的地方,一些不为很多人知道的秘密等。

10.1 为什么不用Cairngorm

Adobe官方文档所提供的顶多可被看做一个怎么在小项目中应用Flex的解释说明。对于大型项目的开发,怎么很好地组织客户端模块的逻辑,并没有一个详细的解释。这可能也就是Adobe收购Cairngorm开源社区的原因。说到底Cairngorm只不过是一个客户端的MVC微架构模式,它并不能解决Flex开发的所有问题。到底用不用它完全取决设计者的意见。况且,Cairngorm架构本身也带来了一些负面的问题。它使客户端的逻辑变得统一和调理,但是同时也使开发变得更复杂。一个用户事件触发响应周期无端地要经过若干个层次才能得到处理。并且Cairngorm的官方文档看起来总是离散的,没有得到及时更新。不过就笔者的经验和教训来讲,利用Cairngorm框架的最大好处是为规模稍微大一些的Flex应用提供了一个整洁的逻辑模块组织方式,但是这个方式并不简洁。

 

10.2 集成CairngormFlex Builder

第一步,在Adobe下载你打算集成的Cairngorm版本。上面提供了若干不同类型的下载连接,下载连接:http://labs.adobe.com/wiki/index.php/Cairngorm:Downloads

注意,资源包有两个类型一个是普通类型,另一个是企业版本类型。企业版集成了LiveCycle Data Services的功能。

第二步:访问http://cairngormdocs.org/blog/?p=18,下载Caringorm文档实例应用CairngormDiagram的资源包。

第三步: FlexBuilder 中创建一个名字为CairngormDiagramWeb应用,

第四步: 将第二步下载的Cairngorm框架支持包Cairngorm.swc添加到此应用的ClassPath下面。并将Main Source Folder的默认src目录结构清空。

第五步: 导入CairngormDiagram实例源代码,

选择导入类型为文件系统

定位到你下载的CairngormDiagram源代码位置,单击Finish.

最后,整个项目看起来是这个样子的。

第六步: 编译运行CairngormDiagram示例应用。

 

10.3 Cairngorm核心组件和工作流程

参考www.cairngormdocs.org上本实例的流程图,Cairngorm的工作流程可分为以下几个步骤。

1.       视图View文件里的用户动作触发一个Cairngorm事件。如按钮点击,初始化完成等等。

2.       在与视图View文件绑定的ViewHelper文件中组装事件传递数据,事件经CairngormEventDispather实例分发到Controller控制层,等待处理。

3.       根据预注册事件处理响应函数,CaringormEventFrontController实例捕捉,并将此事件具体分派到具体事件处理命令Command实例类中进行处理。

4.       Command命令实例的execute()方法被调用,此方法一般首先初始化业务代理实例Delegate,并解析CaringormEvent实例中封装的传递数据,将此数据作为具体Delegate代理方法的输入参数。

5.       Delegate代理方法被调用,请求服务端数据响应。当客户端请求被正确响应时,Command命令对象的onResult()方法异步地监听并接收服务端返回数据,初始化ModuleLocator中具体参数;相反当客户端请求失败时,Command命令对象的onFault()方法异步地监听和接收服务端错误信息。

6.       ModuleLocator中的变量更新后,绑定这些变量的View视图文件被更新,完成了一次用户交互。

 

实际Cairngorm开发过程中,开发者需要编写的文件分别有,

1.       主视图文件CairngormDiagram.mxml

主视图文件定义了整个Flex Cairngorm应用的入口规范和若干关键组件对象的初始化任务。比如针对本例中的主视图文件CairngormDiagram.mxml

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

<mx:Application

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

    xmlns:business="com.adobe.cairngorm.samples.addcontact.business.*"

    xmlns:control="com.adobe.cairngorm.samples.addcontact.control.*"

    xmlns:view="com.adobe.cairngorm.samples.addcontact.view.*"

    pageTitle="Cairngorm Diagram accompanying example code"

    horizontalAlign="center" verticalAlign="middle" viewSourceURL="srcview/index.html">

 

<!--

 

 Cairngorm Diagram companion code

 Based on Login example by Alex Uhlmann - http://www.alex-uhlmann.de/flash/adobe/blog/cairngormlogin/CairngormLogin.html

 

 *** Please submit changes back to the cairngorm-documentation group here

    http://tech.groups.yahoo.com/group/cairngorm-documentation/

    or contact me directly -  Evan Gifford, evan@flexheads.com ***

 

 Diagram can be downloaded here: http://www.flexheads.com/cairngorm/

 

-->

 

    <mx:Script>

    <![CDATA[

               import mx.core.Container;

               import com.adobe.cairngorm.samples.addcontact.model.ModelLocator;   

               import com.adobe.cairngorm.samples.addcontact.vo.ContactVO;

                                             

               [Bindable]

               public var model : ModelLocator = ModelLocator.getInstance();

 

    ]]>

    </mx:Script>

   

<!-- ========================================================================== -->

 

    <!-- the ServiceLocator where we specify the remote services -->

    <business:Services id="addcontactServices"/>

   

    <!-- the FrontController, containing Commands specific to this appliation -->

    <control:AddContactControl id="controller"/>

   

<!-- ========================================================================== -->

 

    <view:AddContactPanel id="addcontactPanel" contacts="{model.contacts}" addcontact="{ model.addcontact }"/>

 

</mx:Application>

本例的主视图文件在装载时初始化三个相关对象,分别是addcontactServices业务对象、AddContactControl命令分发器对象、自定义组件AddContactPanel对象。addcontactServices对象在business业务配置文件Services.xml定义;AddContactControl命令分发器对象准确地预配置了不同CaringormEvent事件的相应事件处理Command;而AddContactPanel组件则定义了Flex应用的主视图的布局。这三个组件一般情况下在主视图文件中都是必须显式生命的。注意,在自定义视图组件AddContactPanel中,绑定了两个ModuleLocator实例中的变量,contactsaddcontacts.也就是说当Command命令的异步回调函数onResult()方法改变这两个变量时,此视图组件将因这些改变的变量数据而更新。另外也需注意这三个组件实例的名字空间定义和引用方式。

 

2.       自定义视图组件AddContactPanel

一般地,Flex的视图层可以根据需要分为任意的自定义组件组合,甚至是嵌套组合,是这些组件一起形成了Cairngorm的视图层。本例中的AddContactPanel就是一个。与标准的Cairngorm应用不同的是,在此示例中为了简便,将View视图文件和ViewHelper辅助类文件和在了一起。在真实的应用中,为了便于将来的维护和组件重用我们一般都还是分开处理的。

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

<mx:Panel

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

   xmlns:view="com.adobe.cairngorm.samples.addcontact.view.*"

   title="MyContacts"

   horizontalAlign="left" height="350">

 

    <mx:Script>

    <![CDATA[ 

              

               import com.adobe.cairngorm.control.CairngormEventDispatcher;    

               import com.adobe.cairngorm.samples.addcontact.control.AddContactEvent;  

               import com.adobe.cairngorm.samples.addcontact.model.AddContact;

               import com.adobe.cairngorm.samples.addcontact.vo.ContactVO;

               import com.adobe.cairngorm.samples.addcontact.model.ModelLocator;

               import mx.collections.ArrayCollection;

 

       [Bindable]

       public var addcontact : AddContact;

      

       [Bindable]

       public var contacts : ArrayCollection; 

        

       public function addContact() : void

       {

          var contactVO : ContactVO = new ContactVO();

          contactVO.fullname = fullname.text;

          contactVO.emailaddress = emailaddress.text;

                      

                       var event : AddContactEvent = new AddContactEvent( contactVO );

                       CairngormEventDispatcher.getInstance().dispatchEvent( event );

       }

    ]]>

    </mx:Script>

    <mx:HBox height="100%">

   

    <mx:VBox>

   

        <mx:Form id="addcontactForm">

       

                   <mx:HBox width="100%" horizontalAlign="left">

                           <mx:Text text="Add a Contact:"/>

                   </mx:HBox>

                  

            <mx:FormItem label="Name: ">

                <mx:TextInput id="fullname"/>

            </mx:FormItem>

           

            <mx:FormItem label="Email: ">

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

            </mx:FormItem>

 

            <mx:HBox width="100%" horizontalAlign="right">

                       <mx:Button label="AddContact" enabled="{ !addcontact.isPending }" click="addContact()"/>  

               </mx:HBox>

              

        </mx:Form>

       

        <mx:Text

                text="{ addcontact.statusMessage }"

                textAlign="center"/>

        </mx:VBox>

 

    <mx:VRule  height="100%" strokeColor="#DDDDDD"/>  

 

    <mx:VBox paddingTop="15" paddingLeft="15" paddingRight="15" paddingBottom="15">

    

      <mx:Text text="List of Contacts"/>

 

      <mx:List wordWrap="true" dataProvider="{contacts}" width="200" height="220"></mx:List>

 

    </mx:VBox>

 

  </mx:HBox>  

</mx:Panel>

本组件扩展了Panel容器组件,在一个Form容器addcontactForm中封装了一个contactVO对象,此对象将在表单提交时打包进一个CairngormEvent中。当用户点击AddContact按钮时,点击事件被触发,addContact()事件响应函数被调用。如前所述,在此函数中,Form表单元素被封装在一个ContactVO中,

var contactVO : ContactVO = new ContactVO();

contactVO.fullname = fullname.text;

contactVO.emailaddress = emailaddress.text;

然后contactVO对象被打包进一个AddContactEventCairngormEvent类型的事件中,CairngormEvent事件就好比是一根线,是它把View,ViewHelper,FrontController,Command,Delegater等珍珠串联起来的。下面我们就会了解到,VO对象是一个类似于javabean一样的数据模型,并且一般是要和服务端DTO数据传输对象是一一对应的。

var event : AddContactEvent = new AddContactEvent( contactVO );

事件被CairngormEventDispatcher对象分发到命令适配器FrontController中等待处理,它是一个单例模式的实例通过getInstance()方法拿到应用的唯一实例。

CairngormEventDispatcher.getInstance().dispatchEvent( event );

 

3. VOValue Object 对象

Cairngorm应用中,各式各样的VO扮演着数据模板的角色,每个VO类模板都要实现ValueObject接口。与服务端数据交互时,Flex将按照这个ValueObject接口序列化此数据对象。类似于JAVA中的javabean.大部分时候我们用它来封装视图组件中相关联的页面元素数据,并且为了能把这些VO传递到服务端,需要显式地指定当前VO和服务端哪个真正的数据传输对象DTO相匹配。

此例中,我们有ContactVO,它定义了三个属性fullname,emailaddress,addcontactDate

package com.adobe.cairngorm.samples.addcontact.vo

{

    import com.adobe.cairngorm.vo.ValueObject;

   

   [Bindable]

    public class ContactVO implements ValueObject

    {

               public var fullname : String;

               public var emailaddress : String;

               public var addcontactDate : Date;

    }

   

}

至于和Java服务端DTO相匹配,在定义时,一定要显式地指出当前VO和服务端的哪个javabean对象相匹配。参考一个示例,比如根据项目需求我们定义了一个LoginUserVO.as

package com.broadview.entities.vo

{

    import com.adobe.cairngorm.vo.ValueObject;

    import mx.collections.ArrayCollection;

    [RemoteClass(alias="com.broadview.entities.dto.LoginUserVO")]

   [Bindable]

    public class LoginUserVO implements ValueObject

    {

               //general user information vo

               public var generalUserInfo:GeneralUserInfo;

               //use to map all the functionalities related to current user.

               public var functionPermissions:Array = [];

               //use to contain all the roles the current user has.

               public var roles:Array =  [];

               //use to store all the org permissions related to current user

               public var organizationPermissions : Array = [];

               //use to store all the module items related to current user

               public var moduleCollection:Array = [];

               //use to store all the base datas related to current user

               public var baseDataCollection:Array = [];

    }

}

而对应的javabean类文件为,

package com.broadview.entities.dto;

import java.util.List;

public class LoginUserVO

{

               //general user information vo

               private GeneralUserInfo generalUserInfo;

               //use to map all the functionalities related to current user.

               public Object[] functionPermissions;

               //use to contain all the roles the current user has.

               public Object[] roles;

               //use to store all the org permissions related to current user

               public Object[] organizationPermissions;

               //use to store all the module items related to current user

               public Object[] moduleCollection;

               //use to store all the base datas related to current user

               public Object[] baseDataCollection;

               /**

                * @return the baseDataCollection

                */

               public Object[] getBaseDataCollection() {

                       return baseDataCollection;

               }

               /**

                * @param baseDataCollection the baseDataCollection to set

                */

               public void setBaseDataCollection(Object[] baseDataCollection) {

                       this.baseDataCollection = baseDataCollection;

               }

               /**

                * @return the functionPermissions

                */

               public Object[] getFunctionPermissions() {

                       return functionPermissions;

               }

               /**

                * @param functionPermissions the functionPermissions to set

                */

               public void setFunctionPermissions(Object[] functionPermissions) {

                       this.functionPermissions = functionPermissions;

               }

               /**

                * @return the generalUserInfo

                */

               public GeneralUserInfo getGeneralUserInfo() {

                       return generalUserInfo;

               }

               /**

                * @param generalUserInfo the generalUserInfo to set

                */

               public void setGeneralUserInfo(GeneralUserInfo generalUserInfo) {

                       this.generalUserInfo = generalUserInfo;

               }

               /**

                * @return the moduleCollection

                */

               public Object[] getModuleCollection() {

                       return moduleCollection;

               }

               /**

                * @param moduleCollection the moduleCollection to set

                */

               public void setModuleCollection(Object[] moduleCollection) {

                       this.moduleCollection = moduleCollection;

               }

               /**

                * @return the organizationPermissions

                */

               public Object[] getOrganizationPermissions() {

                       return organizationPermissions;

               }

               /**

                * @param organizationPermissions the organizationPermissions to set

                */

               public void setOrganizationPermissions(Object[] organizationPermissions) {

                       this.organizationPermissions = organizationPermissions;

               }

               /**

                * @return the roles

                */

               public Object[] getRoles() {

                       return roles;

               }

               /**

                * @param roles the roles to set

                */

               public void setRoles(Object[] roles) {

                       this.roles = roles;

               }

}

那么这里就牵扯一个FlexJava数据类型匹配的问题。具体类型匹配列表可以查阅Flex官方文档。不过在真实的项目开发中,我们为方便开发甚至会手写一个从JAVAActionScriptVO脚本产生函数。

 

4.       CairngormEvent对象

CairngormEvent封装了一次用户交互动作以及相关的请求数据。开发者可以根据需要自定义各种各样的CaringormEvent类型事件。当这个事件定义后,就可以在View视图层的ViewHelper辅助类中被CaringormEventDispatcher对象分发到前端控制器FrontController,在那里遍历出当前事件的事件响应命令类Command.Command中,CairngormEvent事件数据被解析,并委托代理类Delegate实现远程服务端的请求和响应。 比如在本例中我们有AddContactEvent事件。基本定义方式是将事件数据当作一个类公共变量,并定义一个利用此数据作为输入参数的构造器,另外构造器中都需要显式地调用其父类CairngormEvent的构造器方法super()

package com.adobe.cairngorm.samples.addcontact.control

{

    import com.adobe.cairngorm.control.CairngormEvent;

    import com.adobe.cairngorm.samples.addcontact.vo.ContactVO;

   

    /**

     * The Cairngorm event broadcast when the user attempts to log in

     */

    public class AddContactEvent extends CairngormEvent

    {

               /**

                * The addcontact details for the user

                */

               public var contactVO : ContactVO;

                              

               /**

                * The constructor, taking a ContactVO

                */

               public function AddContactEvent( contactVO : ContactVO )

               {

                       super( AddContactControl.EVENT_ADD_CONTACT );

                       this.contactVO = contactVO;

               }

    }         

}

如前所说,如果CaringormEvent需要封装请求参数,一定要像上面的格式定义这个事件。如果没有请求数据需要封装,那么就没有必要定义这个事件类了,可以直接用普通的CairngormEvent事件对象。这种定义格式就像一种契约,没有什么神秘的地方。参考另外一个示例,

package com.broadview.flex.control

{

    import com.adobe.cairngorm.control.CairngormEvent;

    import com.broadview.flex.vo.GeneralUserInfo;

   

    /**

     * The Cairngorm event broadcast when the user attempts to log in

     */

    public class LoginEvent extends CairngormEvent

    {

               /**

                * The login details for the user

                */

               public var loginVO : GeneralUserInfo;

                              

               /**

                * The constructor, taking a LoginVO

                */

               public function LoginEvent( loginVO : GeneralUserInfo )

               {

                       super( SystemControl.EVENT_LOGIN );

                       this.loginVO = loginVO;

               }

    }         

}

 

5. FrontController前端控制器

FrontController在功能上相当于一个事件适配器,这个类中定义了与每个CairngormEvent事件一一对应的Command命令处理类。本例中我们有一个AddContactControlFrontController,

package com.adobe.cairngorm.samples.addcontact.control

{

    import com.adobe.cairngorm.control.FrontController;

    import com.adobe.cairngorm.samples.addcontact.commands.AddContactCommand;

   

    public class AddContactControl extends FrontController

    {

               public function AddContactControl()

               {

                       addCommand( AddContactControl.EVENT_ADD_CONTACT, AddContactCommand );

               }

              

               public static const EVENT_ADD_CONTACT : String = "addcontact";

    }

}

在这个命令适配器中,我们曾经自定义的每个CairngormEvent事件都要在此注册,并且提供其相对应的命令类AddContactCommand. 只有注册后的CairngormEvent事件才能够最终分发到Command命令类。事件命令的匹配可能有很多个,不过格式都是一样的。参考示例,

package com.broadview.flex.control

{

    import com.adobe.cairngorm.control.FrontController;

    import com.broadview.flex.commands.LoginCommand;

    import com.broadview.flex.commands.PreLoadOfficeListCommand;

    import com.broadview.flex.commands.PreLoadRoleListCommand;

    import com.broadview.flex.commands.PreLoadUserListCommand;

    import com.broadview.flex.commands.RetrieveLangListCommand;

    import com.broadview.flex.commands.UpdateOrAddOfficeCommand;

    import com.broadview.flex.commands.UpdateOrAddRoleCommand;

    public class SystemControl extends FrontController

    {

               public function SystemControl()

               {

                       addCommand( SystemControl.EVENT_LOGIN, LoginCommand );

                       addCommand( SystemControl.EVENT_PRE_RETRIEVE_LANGAGES, GetListCommand);

                   addCommand( SystemControl.EVENT_PRE_LOAD_ROLE_LIST,PreLoadRoleListCommand);

                   addCommand( SystemControl.EVENT_UPDATE_OR_ADD_ROLE,UpdateOrAddRoleCommand);

                   addCommand( SystemControl.EVENT_PRE_LOAD_OFFICE_LIST,PreLoadListCommand);

                   addCommand( SystemControl.EVENT_UPDATE_OR_ADD_OFFICE,UpdateOrAddCommand);

                       addCommand( SystemControl.EVENT_PRE_LOAD_USER_LIST, PreLoadUserCommand);

               }

              

               public static const EVENT_LOGIN : String = "login";

               public static const EVENT_PRE_RETRIEVE_LANGAGES : String = "retrievelanguages";

               public static const EVENT_PRE_LOAD_ROLE_LIST:String = "preloadrolelist";

               public static const EVENT_UPDATE_OR_ADD_ROLE:String = "roleupdateoradd";

              

               public static const EVENT_PRE_LOAD_OFFICE_LIST:String = "preloadofficelist";

               public static const EVENT_UPDATE_OR_ADD_OFFICE:String = "updateoraddoffice";

              

               public static const EVENT_PRE_LOAD_USER_LIST: String = "preloaduserlist";

    }

}

 

6.Command命令类

CairngormEvent事件最终将在其相匹配的Command命令类中得到处理,Command命令类基本上包括三个方法,在execute()方法中,我们一般都是初始化Module层的Delegate对象,将请求代理到远程服务端的不同代理Service业务。另外两个方法为异步回调函数,onResult()onFault(),这两个方法主要是监听服务端响应,接收和处理响应数据等。本例中我们有AddContactCommand命令类,

package com.adobe.cairngorm.samples.addcontact.commands

{

    import mx.rpc.events.ResultEvent;

    import com.adobe.cairngorm.business.Responder;

    import com.adobe.cairngorm.commands.Command;

    import com.adobe.cairngorm.control.CairngormEvent;

    import com.adobe.cairngorm.samples.addcontact.business.AddContactDelegate;

    import com.adobe.cairngorm.samples.addcontact.control.AddContactEvent;

    import com.adobe.cairngorm.samples.addcontact.model.ModelLocator;

    import com.adobe.cairngorm.samples.addcontact.vo.ContactVO;

   

    public class AddContactCommand implements Command, Responder

    {

               private var model : ModelLocator = ModelLocator.getInstance();

 

               public function execute( event : CairngormEvent ) : void

               {

                       model.addcontact.isPending = true;

                      

                       var delegate : AddContactDelegate = new AddContactDelegate( this );  

                       var addcontactEvent : AddContactEvent = AddContactEvent( event ); 

                       delegate.addcontact( addcontactEvent.contactVO );          

               }

                 

               public function onResult( event : * = null ) : void

               {                     

 

                       model.addcontact.contactVO = ContactVO( event );

                       model.addcontact.isPending = false;

                      

                       //Simplify - Used an array of strings instead of ContactVO's

                       model.contacts.addItem(ContactVO(event).fullname+" "+ContactVO(event).emailaddress);

               }

                              

               public function onFault( event : * = null ) : void

               {

                       model.addcontact.statusMessage = "Could not send contact information to the server.";

                       model.addcontact.isPending = false;

               }

    }

}

 

7.       业务代理Delegate

所有与远程服务端的交互都是通过自定义的业务代理类Delegate来实现的。Delegate类中指定了此次远程请求服务的方法名字、参数、返回数据等信息。本例中我们有AddContactDelegate代理类,

package com.adobe.cairngorm.samples.addcontact.business

{

    import com.adobe.cairngorm.business.Responder;

    import com.adobe.cairngorm.business.ServiceLocator;

    import com.adobe.cairngorm.samples.addcontact.vo.ContactVO;

   

    import flash.utils.setTimeout;

   

    import mx.rpc.AsyncToken;

    import mx.rpc.events.ResultEvent;

   

    public class AddContactDelegate

    {

               private var responder : Responder;

               private var service : Object;

                              

               public function AddContactDelegate( responder : Responder )

               {

                       this.service = ServiceLocator.getInstance().getService( "addcontactService" );

                       this.responder = responder;

               }

              

               public function addcontact( contactVO : ContactVO ): void

               {

                       var token : AsyncToken = service.addcontact( contactVO );

                       token.resultHandler = responder.onResult;

                       token.faultHandler = responder.onFault;

                      

                       //for demo purpose: simulate remote service result

                       //setTimeout( addcontactResult, 1000, contactVO );

               }

              

               private function addcontactResult( contactVO : ContactVO ): void

               {

                       if( 1 )

                       {

                               responder.onResult( contactVO );

                       }

                       else

                       {

                               responder.onFault();

                       }                     

               }      

    }

}

在实际的应用中一类业务代理逻辑通常放在一个Delegate类中,所有的Command命令请求的代理方法都应该在Delegate中声明。比如我们有一个SystemDelegate系统代理对象,它主要负责一类与系统管理相关的业务代理方法,

package com.broadview.flex.business

{

    import com.adobe.cairngorm.business.Responder;

    import com.adobe.cairngorm.business.ServiceLocator;

    import com.broadview.flex.vo.GeneralUserInfo;

    import com.broadview.flex.vo.OrganizationVO;

    import com.broadview.flex.vo.SystemRoleVO;

   

    import mx.controls.Alert;

    import mx.rpc.AsyncToken;

    import mx.rpc.remoting.RemoteObject;

   

    public class SystemDelegate

    {

               private var responder : Responder;

               private var service : Object;

                              

               public function SystemDelegate( responder : Responder )

               {

                   this.service=ServiceLocator.getInstance().getService("systemService") as RemoteObject;

                       this.responder = responder;

               }

 

               public function login( loginVO : GeneralUserInfo ): void

               {

                       var token : AsyncToken = service.login( loginVO );

                       token.resultHandler = responder.onResult;

                       token.faultHandler = responder.onFault;

               }      

              

               public function roleUpdateOrAdd(roleVO:SystemRoleVO):void{

                       var token : AsyncToken =  service.roleUpdateOrAdd( roleVO );

                       token.resultHandler = responder.onResult;

                       //token.faultHandler = responder.onFault;

               }

              

               public function preLoadRoleList():void{

                       var token : AsyncToken = service.preLoadRoleList();

                       token.resultHandler = responder.onResult;

                       token.faultHandler = responder.onFault;

               }

              

               public function preLoadOfficeList():void{

                       var token : AsyncToken = service.preLoadOfficeList();

                       token.resultHandler = responder.onResult;

                       token.faultHandler = responder.onFault;

               }

              

               public function preLoadUserList():void{

                       var token : AsyncToken = service.preLoadUserList();

                       token.resultHandler = responder.onResult;

                       token.faultHandler = responder.onFault;

               }

              

               public function officeUpdateOrEdit(officeVO:OrganizationVO):void{

                       //mx.controls.Alert.show('**************delegate**********');

                       var token : AsyncToken = service.officeUpdateOrEdit(officeVO);

                       token.resultHandler = responder.onResult;

                       token.faultHandler = responder.onFault;

               }

    }

}

 

8. ServiceLocator类文件

这个文件通常以MXML标签的形式声明此应用所有可能调用到的远程服务端服务。服务的类型可以有多样,我们着重介绍Remote Object方式,在本示例中我们用到了一种名字叫做addcontactService,这个业务,首先是在Flex应用配置文件remoting-config.xml中配置和声明的。

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

<service id="remoting-service"

    class="flex.messaging.services.RemotingService"

    messageTypes="flex.messaging.messages.RemotingMessage">

 

    <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="addcontactService">

        <properties>

               <factory>spring</factory>

            <source>addcontactService</source>

        </properties>

    </destination>

</service>

 

addcontactServiceremoting-config.xml配置后,才能在业务ServiceLocator对象的订阅这个业务。

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

<cairngorm:ServiceLocator

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

    xmlns:cairngorm="com.adobe.cairngorm.business.*">

    <!--commented for demo-->

   

    <mx:RemoteObject

               id="addcontactService"

               destination="addcontactService"

               showBusyCursor="true"

               result="event.token.resultHandler( event );"

               fault="event.token.faultHandler( event );">

    </mx:RemoteObject>        

 

</cairngorm:ServiceLocator>

 

9. ModuleLocator对象

ModuleLocator对象采用了单例Singlton的模式。整个应用只会有唯一的ModuleLocator的对象,用来管理和维护系统数据。它就像一个全局变量管理池,通常在Command命令类的onResult()方法中初始化和更新ModuleLocator中的数据。数据更新后,绑定这些数据的视图组件也同步地被更新,对于本例,ModuleLocator对象定义了两个变量,addcontactcontacts.

addContactCommand更新contacts变量后,绑定了contacts变量的addContactsPanel视图文件也会被同时更新。

package com.adobe.cairngorm.samples.addcontact.model

{

    import com.adobe.cairngorm.model.ModelLocator;

    import mx.collections.ArrayCollection;

 

    [Bindable]

    public class ModelLocator implements com.adobe.cairngorm.model.ModelLocator

    {

               private static var modelLocator : com.adobe.cairngorm.samples.addcontact.model.ModelLocator;

              

               public static function getInstance() : com.adobe.cairngorm.samples.addcontact.model.ModelLocator

               {

                       if ( modelLocator == null )

                               modelLocator = new com.adobe.cairngorm.samples.addcontact.model.ModelLocator();

                              

                       return modelLocator;

       }

      

   public function ModelLocator()

   {

               if ( com.adobe.cairngorm.samples.addcontact.model.ModelLocator.modelLocator != null )

                               throw new Error( "Only one ModelLocator instance should be instantiated" );      

   }

              

               public var addcontact : AddContact = new AddContact();

               public var contacts : ArrayCollection = new ArrayCollection();

    }         

}

 

10. 后台服务端逻辑

后台的实现根据客户端请求的不同分为若干类,我们只介绍利用java怎么写服务端逻辑。对于Remoting Object类型调用,在服务端通常要实现这个远程对象。我们将在下一章详细讲解怎么利用Spring框架和Hibernate来实现服务端逻辑。

10.4 真实系统中的Caringrom

和上述示例有些不同的地方,在真实系统中我们通常还会利用Cairngorm框架提供的一些更高级的功能。

一.     ViewHelper辅助类文件。ViewHelper通常是和View视图文件一一对应的。ViewHelper主要处理所有与其相关的View视图文件所有的ActionScript逻辑。这样做的好处是,我们不必在View视图文件中利用<mx:Script>编辑任何ActionScript逻辑。视图文件只负责页面布局,ViewHelper文件则负责视图文件的数据处理、页面逻辑操作等。

二.     利用Module模块封装View视图文件。 在实际的应用中,View视图文件可能很多,也可能很复杂。为了避免系统初始化是较大的初始化下载,开发者通常将View视图文件封装到预编译的Module模块中。

三.     合理利用ViewLocator对象,这是一个View视图上下文的管理类它封装了全部视图层的逻辑。利用ViewLocator可以很好地分离应用视图层和控制层。

 

10.4.1 AddContactPanelView视图文件

更改视图文件为Module模块,移植ActionScript逻辑到其辅助类AddContactModuleHelper中,将辅助类注册到ViewLocator对象中

<view:AddContactModuleHelper id="addContactModuleHelper"/>

另外要注意重构后数据绑定的方法和名字空间的声明引用。

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

<mx:Module

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

   xmlns:view="com.broadview.flex.view.*"

   horizontalAlign="left" height="350">

   <view:AddContactModuleHelper id="addContactModuleHelper"/>

   <mx:HBox height="100%">

   <mx:VBox>

        <mx:Form id="addcontactForm">

       

                   <mx:HBox width="100%" horizontalAlign="left">

                           <mx:Text text="Add a Contact:"/>

                   </mx:HBox>

                  

            <mx:FormItem label="Name: ">

                <mx:TextInput id="fullname"/>

            </mx:FormItem>

           

            <mx:FormItem label="Email: ">

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

            </mx:FormItem>

 

            <mx:HBox width="100%" horizontalAlign="right">

                       <mx:Button label="AddContact" enabled="{ !addContactModuleHelper.getModelLocator().addcontact.isPending }" click="addContactModuleHelper.addContact()"/>  

               </mx:HBox>

              

        </mx:Form>

       

        <mx:Text

                text="{ addContactModuleHelper.getModelLocator().addcontact.statusMessage }"

                textAlign="center"/>

        </mx:VBox>

 

    <mx:VRule  height="100%" strokeColor="#DDDDDD"/>  

 

    <mx:VBox paddingTop="15" paddingLeft="15" paddingRight="15" paddingBottom="15">

    

      <mx:Text text="List of Contacts"/>

      <mx:List wordWrap="true" dataProvider="{addContactModuleHelper.getModelLocator().contacts}" width="200" height="220"></mx:List>

    </mx:VBox>

  </mx:HBox>  

</mx:Module>

 

10.4.2 AddContactModuleHelper辅助类文件

AddContactModuleHelper类集成了Cairngorm基础类ViewHelper,一个业务方法addContact()和一个测试ViewLocator对象的方法doViewLocatorTest(),

package com.broadview.flex.view

{

    import com.adobe.cairngorm.control.CairngormEventDispatcher;

    import com.adobe.cairngorm.view.ViewHelper;

    import com.broadview.flex.control.AddContactEvent;

    import com.broadview.flex.model.AddContact;

    import com.broadview.flex.vo.ContactVO;

    import com.broadview.flex.model.SampleModelLocator;

    import mx.collections.ArrayCollection;

    import mx.controls.Alert;

    public class AddContactModuleHelper extends ViewHelper

    {

       [Bindable]

       public var addcontact : AddContact;

      

       [Bindable]

       public var contacts : ArrayCollection; 

        

       public function addContact() : void

       {

          var contactVO : ContactVO = new ContactVO();

          contactVO.fullname = this.view.fullname.text;

          contactVO.emailaddress = this.view.emailaddress.text;

                 var event : AddContactEvent = new AddContactEvent( contactVO );

                 CairngormEventDispatcher.getInstance().dispatchEvent( event );

       }

              

               public function AddContactModuleHelper()

               {

                       super();

               }

               public function getModelLocator():SampleModelLocator{

                       return SampleModelLocator.getInstance();

               }

               public function doViewLocatorTest():void{

                       mx.controls.Alert.show("view locator has been invoked.");

               }

    }

}

 

10.4.3 AddContactCommand命令类文件

在此类中有个测试方法doViewLocatorTest(),这个方法主要展示在Command命令类中怎样调用辅助类AddContactModuleHelper中定义的公共方法。

package com.broadview.flex.commands

{

    import mx.rpc.events.ResultEvent;

    import com.adobe.cairngorm.business.Responder;

    import com.adobe.cairngorm.commands.Command;

    import com.adobe.cairngorm.control.CairngormEvent;

    import com.broadview.flex.business.AddContactDelegate;

    import com.broadview.flex.control.AddContactEvent;

    import com.broadview.flex.model.SampleModelLocator;

    import com.broadview.flex.vo.ContactVO;

    import com.adobe.cairngorm.view.ViewLocator;

    import com.broadview.flex.view.AddContactModuleHelper;

   

    public class AddContactCommand implements Command, Responder

    {

               private var model : SampleModelLocator = SampleModelLocator.getInstance();

 

               public function execute( event : CairngormEvent ) : void

               {

                       model.addcontact.isPending = true;

                      

                       var delegate : AddContactDelegate = new AddContactDelegate( this );  

                       var addcontactEvent : AddContactEvent = AddContactEvent( event ); 

                       delegate.addcontact( addcontactEvent.contactVO );          

               }

                 

               public function onResult( event : * = null ) : void

               {                     

 

                       model.addcontact.contactVO = ContactVO( event );

                       model.addcontact.isPending = false;

                      

                       //Simplify - Used an array of strings instead of ContactVO's

                       model.contacts.addItem(ContactVO(event).fullname+" "+ContactVO(event).emailaddress);

                      

                   AddContactModuleHelper(ViewLocator.getInstance().getViewHelper("addContactModuleHelper")).doViewLocatorTest();

               }

                              

               public function onFault( event : * = null ) : void

               {

                       model.addcontact.statusMessage = "Could not send contact information to the server.";

                       model.addcontact.isPending = false;

               }

    }

}

 

10.4.4 主视图文件CairngormDiagrom.mxml文件

这个文件展示了在视图文件中引用自定义Module模块的方法,

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

<mx:Application

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

    xmlns:business="com.broadview.flex.business.*"

    xmlns:control="com.broadview.flex.control.*"

    xmlns:view="com.broadview.flex.view.*"

    pageTitle="Cairngorm Diagram accompanying example code"

    horizontalAlign="center" verticalAlign="middle" viewSourceURL="srcview/index.html">

 

<!--

 

 Cairngorm Diagram companion code

 Based on Login example by Alex Uhlmann - http://www.alex-uhlmann.de/flash/adobe/blog/cairngormlogin/CairngormLogin.html

 

 *** Please submit changes back to the cairngorm-documentation group here

    http://tech.groups.yahoo.com/group/cairngorm-documentation/

    or contact me directly -  Evan Gifford, evan@flexheads.com ***

 

 Diagram can be downloaded here: http://www.flexheads.com/cairngorm/

 

-->

 

    <mx:Script>

    <![CDATA[

               import mx.core.Container;

               import com.broadview.flex.model.SampleModelLocator;  

               import com.broadview.flex.vo.ContactVO;

                                             

               [Bindable]

               public var model : SampleModelLocator = SampleModelLocator.getInstance();

 

    ]]>

    </mx:Script>

   

<!-- ========================================================================== -->

   

    <!-- the ServiceLocator where we specify the remote services -->

    <business:Services id="addcontactServices"/>

   

    <!-- the FrontController, containing Commands specific to this appliation -->

    <control:AddContactControl id="controller"/>

   

<!-- ========================================================================== -->

 

    <!--view:AddContactPanelView id="addcontactPanel" contacts="{model.contacts}" addcontact="{ model.addcontact }"/-->

    <mx:ModuleLoader url="/bin/com/broadview/flex/view/AddContactPanelView.swf"/>

</mx:Application>

 

 

 

                                      

原创粉丝点击