您的位置:首页 > 大数据 > 人工智能

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

2008-10-07 11:42 267 查看

第十一章 深入理解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 集成Cairngorm到Flex Builder

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



注意,资源包有两个类型一个是普通类型,另一个是企业版本类型。企业版集成了LiveCycle Data Services的功能。
第二步:访问http://cairngormdocs.org/blog/?p=18,下载Caringorm文档实例应用CairngormDiagram的资源包。
第三步: 在FlexBuilder 中创建一个名字为CairngormDiagram的Web应用,



第四步: 将第二步下载的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. 根据预注册事件处理响应函数,CaringormEvent被FrontController实例捕捉,并将此事件具体分派到具体事件处理命令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实例中的变量,contacts和addcontacts.也就是说当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对象被打包进一个AddContactEvent的CairngormEvent类型的事件中,CairngormEvent事件就好比是一根线,是它把View,ViewHelper,FrontController,Command,Delegater等珍珠串联起来的。下面我们就会了解到,VO对象是一个类似于javabean一样的数据模型,并且一般是要和服务端DTO数据传输对象是一一对应的。

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

CairngormEventDispatcher.getInstance().dispatchEvent( event );

3. VO(Value 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;
}
}
那么这里就牵扯一个Flex和Java数据类型匹配的问题。具体类型匹配列表可以查阅Flex官方文档。不过在真实的项目开发中,我们为方便开发甚至会手写一个从JAVA到ActionScript的VO脚本产生函数。

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命令处理类。本例中我们有一个AddContactControl的FrontController,

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>

addcontactService在remoting-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对象定义了两个变量,addcontact和contacts.
当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[/b]视图文件[/b]
更改视图文件为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[/b]辅助类文件[/b]
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[/b]命令类文件[/b]
在此类中有个测试方法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>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: