基于Ciarngorm微结构开发Flex RIAs

前台控制器监听用户动作

用户处于这次会话的主导地位。你的RIA等待来自用户的一些提示。这些提示包括:点击按钮,拖放图标,双击行或是发送表单,这些都叫做“用户动作”

Cairngorm 将这些用户动作翻译成Cairngorm事件。无论是点击,按下,拖曳,放下,提交的事件都代表用户的需求,你使用事件广播器广播事件。事件广播器是Cairngorm会话的开始。

前台控制器模块是Cairngorm事件的唯一监听者。聚集不同事件句柄,前台控制器确保满足用户的需求。

无论如何,前台控制器不做具体的工作。它只是一个管理者,不是一个工人。前台控制器负责管理着一份名单“谁做什么?”,一份命令名单,命令对应着相应的事件。

命令做全部的工作

一旦前台控制器发现一个事件对应的命令,它将告诉命令去执行。前台控制器告诉任一或每个命令去做什么以一种一致的方式:前台控制器调用命令中的execute()方法,但命令会做它自己特殊的事。

将服务器端的业务逻辑委派给业务代理

很多情况下,命令需要服务器支持。在RIA中,许多业务逻辑执行在客户端。但是或多或少,你需要将不些业务逻辑移到服务器端执行。

命 令不关心一些事是如何完成的。它们只关心它完成了。命令更喜欢将服务器端的业务逻辑委派给其它类。这是好事,毕竟,一些命令要实现远程任务,它们都会需要 相同的服务。另外,对全部命令来说,事务逻辑还没有写在服务器端。可能命令只是简单调用虚构的业务代理类,服务器端结构中并不存在这样的类,但是业务代理 提供了一种接口,以备未来使用。

所以不论何时你有业务逻辑,将它放在业务代理类中,这样你的命令可以调用其中的方法。

业务代理在服务定位器中寻找服务

业务代理为命令和在服务器端的服务间提供了一种完美的接口。这些服务可能是Java服务,ColdFusion组件,Web Services,或是简单的HTTP服务。业务代理对待这些服务都一样,当命令提出时调用它们,当服务是可用时立即返回结果给命令。

无论如何,这些服务的具体实现(Java类名或是CFCs,无论它们有名称或是没名称的服务,WSDL文件,或是HTTP服务的URLs)都装入一个服务库里,叫做服务定位器。

服务定位器是你的RIA需要的全部服务的目录。因为只有业务代理有权调用服务,只有业务代理使用服务定位器在调用这些服务前按名称定位服务(不关心服务的实现),返回结果给命令类。

记住,任何一个命令类(负责响应服务调用,从业务代理中返回数据)都必须实现响应接口。这样,业务代理知道命令有一个onResult()方法来获取全部结果,一个onFault()方法来截获错误。

以Value Objects来传输数据

数据在客户端和服务器端传输时会发生什么,在客户端又如何?Cairngorm强调你可以处理数据可以稍微自由些,将它装入Value Objects,Value Objects强调了数据在业务形式上是什么(它是规则),而不是技术形式上是什么(包含字符和数字对象的数组)。

模型定位器中的存储状态和模型关联视图

最后,你的应用程序必须与用户交流。在Cairngorm中,模型定位器保存着全部应用程序状态。虽然我们不关心用户的体验是如何用MXML和ActionScript实现的,但是我们强调在任何地方视图总是动态的,视图使用数据绑定将数据绑定到模型定位器。

模型定位器只包含Value Objects.当你的命令类使用业务代理去调用服务器端的业务逻辑时,它实现了响应接口,在onResult()方法实现接收Value Objects或是Value Objects集合。

Cairngorm应用程序会话的最后部分引起的结果是命令更新了模型定位器中的用Value Objects表示的新状态。模型定位器使用数据绑定来通知视图,用户接口更新来显示新的数据,完成用户会话。

以上我冗长的描述是关于通过Cairngorm架构不同设计模块的控制流。任一或是每个在应用程序中的feature都按照相同的执行过程,用户动作生成事件,事件调用服务,数据传回模型。

向Cairngorm应用程序添加新的Feature

当你向Cairngorm应用程序加入一个新的feature时,它的过程很简单:

1.注册一个事件和命令,用前台控制器使用addCommand()方法

2.实现一个新的命令如下:

a.实现execute()方法

b.实现onResult()方法从服务器中获取结果

i.用于更新模型定位器的任何结果

ii.模型定位器自动更新视图。

3.加入任何新的服务调用,在命令需要业务代理时。

4.如果这些服务调用需要新的服务时,把它们加入到服务定位器中。

另外,你需要创建Value Objects用于在命令与业务代理间,业务代理与服务器间传输,或者存储到模型定位器中。更特别的,你的ValueObjects可以在你开发应用程序前就存在,你可以不断重用它们,当你向应用程序加入新的feature时。

相似的,当你的应用程序不断扩大时,服务器端服务很快就禁闭了,应用程序开发速度更快,如下:

1.注册一个事件和命令,用前台控制器使用addCommand()方法。

2.实现一个新的命令。

这真的就是全部的。

调试一个Cairngorm应用程序

此外,调试一个Cairngorm应用程序按照相同的可见步骤。如果一个需求模块不能响应用户的动作,调试过程总是这样:

1.检查事件是否在前台控制器中注册过。

2.检查命令中的execute()方法是否被控制器调用。

3.检查相应的代理方法是否被调用。

4.检查命令中的onResult()方法是否被调用。

5.检查模型定位器中的模型是否更新。

用这五步骤,你可以很轻松地分离问题,并修复它。

一个feature经常出现在电子商务应用程序中,它叫做“需求列表”,一种产品的临时列表用于暂时存放用户以后可能要买的产品但还未加入到购物车中。

在一个灵活的团队中,需求列表的需求或是说“story”,在CairngormStore中可以这样理解:
CairngormStore 需求列表

客户除了可以把产品从商店加入到购物篮外,也可以使用需求列表feature来存放他们以后想要买的产品。

使用tab接口,客户可以选择把产品放入到购物篮或是需求列表。当客户购买购物篮中的产品时,在需求列表中的产品不会出现。

客户可以使用拖曳产品的图标或是产品列表的描述的方法加入到需求列表,也可以拖曳产品细则面板中的图标的方法加入到需求列表,还可以按产品细则面板中的“添加”按钮的方法加入到需求列表中。

注意:这种需求不能规定客户可能把物品在需求列表和检验中互相移动。现在假设这种需求是额外的story。

在用户经历之前,这不值得一提。选择tab接口是功能的一种,由J2EE开发人员实现。在现实世界的工程里,我会使用考虑用户经验,结合需求列表,更加有效地加入到全部的信息架构中去。

作为一种典型的灵活工作,开发人员通常将一个story分为一些小任务。使用Cairngorm微架构,存在于feature中的任务变得更加可见,可重用。你可能记得在第四章中我讨论过基于feature驱动的开发。

对于上述的需求列表story,我建议story中的任务是这样的:

在前台控制器类中注册一个新的事件addProductToWishList

创建一个新一命令AddProductToWishList来响应事件。

在模型定位器中创建一个WishList对象

实现视图:

将WishList对象加入到Tab Navigator控制中去,跟Shopping Card对象同个地方

动态绑定WishList对象到模型定位器中。

当用户按下“添加到需求列表”按钮时,广播用户动作到前台控制器。

当用户拖曳和放下产品到需求列表对象时,广播用户动作到前台控制器。

让我们遵循这些任务来编码吧!

在前台控制器类中注册事件

第一步是给基于Cairngorm应用程序简要的描述一下,当用户想要把产品加入到他们的需求列表中时,一个新的用户动作就会发生了。

CairngormStore有它自己的前台控制实例,ShopController.as,你可以使用它注册所有可能的CairngormStore命令。

首先,你必须把事件的名称加入到ShopController

public static var EVENT_ADD_PRODUCT_TO_WISH_LIST = "addProductToWishList";

记住,使用static类型定义事件的名称,你可以在编译时间检查事件被广播时是否合法。这确保当被广播为black-hole之前你没发现事件名称拼写错误时,你的应用程序也不会失败。

接着,你必须注册一个命令类来响应这个事件。这个工作只要在initialiseCommands()方法中加入新的实例:

addCommand( ShopController.EVENT_ADD_PRODUCT_TO_WISH_LIST, new AddProductToWishListCommand() );

这就是前台控制器类实例关心的。现在它认识新的事件,它也知道当这个事件发生时怎么做。

再接下来,你要创建命令类来响应这个事件:AddProductToWishList 命令类。

创建AddProductToWishListCommand命令类

让我们来看看新命令类的代码,它很简单,不需要加以说明:

import org.nevis.cairngorm.commands.Command;

import org.nevis.cairngorm.control.Event;

import org.nevis.cairngorm.samples.store.model.ModelLocator;

import org.nevis.cairngorm.samples.store.vo.ProductVO;



class org.nevis.cairngorm.samples.store.command.AddProductToWishListCommand implements Command

{

public function execute( event : Event ):Void

{

   var product : ProductVO = ProductVO( event.data.product );

   var quantity : Number = Number( event.data.quantity );

   ModelLocator.wishList.addElement( product, quantity );

}

}

这代码一点也不复杂…execute()方法是一个独立的命令实例指针,当ShopController. EVENT_ADD_PRODUCT_TO_WISH_LIST发生时,由前台控制器调用。

Execute()方法简单地添加了适当的产品数量包括当用户按下“添加到需求列表”时将需求列表对象加入到模型定位器。

再是,你现在可以假定用户接口,视图,通过Flex数据绑定到在模型定位器中的需求列表对象。这就是业务逻辑形式下所有要做的事。

下一步是确定我们有需求列表对象在模型定位器上。

存储需求列表对象到模型定位器

Cairngorm使得Flex的开发很简单。自从你有ShoppingCart类在CairngormStore中管理产品列表和这些产品的数量时,你完全可以简单地重用这个类来实现需求列表对象。需求列表对象毕竟是一个简单的ShoppingCart,你不必要对这个特别的访问做检查。

下面是加入到ModelLocator.as的代码:

非常简单,你创建一个新的ShoppingCart实例,然后调用需求列表实例。你在模型定位器的构造器中创建新的实例,如下:

ModelLocator.wishList = new ShoppingCart();

假设你在模型定位器中其它定义中声明了需求列表实例,像这样:

public static var wishList : ShoppingCart;

这真的是全部的工作。你是否感到你可以快速地向Cairngorm应用程序加入新的feature。

实现视图

这样我们创建了一个命令类,在前台控制器类中注册,当产品加入到需求列表中时它更新模型定位器类,你重用了业务对象(ShoppingCart)来实现我们的需求列表。

在为需求列表创建用户接口时剩下的问题就是确保它动态地绑定到模型定位器中的动态数据,确保事件ShopController.EVENT_ADD_PRODUCT_TO_WISH_LIST在任何用户动作发生时返回结果。

将需求列表加入到视图

我将替换屏幕的一部分,只用包含两个ShoppingCart MXML组件的Tab Navigator显示ShoppingCart MXML组件。第一个实例是我们原始的ShoppingCart,而第二个则是需求列表。

当用户发出添加产品到需求列表的动作时,你将改变ProductDetails组件来截获,就像添加产品到ShoppingCart一样。这就需要ProductDetails MXML组件要有一个addProductToWishList事件,下面是代码:

这个简单的需求,只要改动一下SideArea.mxml,如下:

<details:ProductDetails

        id="productDetailsComp"

        width="100%" height="325"

        currencyFormatter="{ ModelLocator.currencyFormatter }"

        selectedItem="{ ModelLocator.selectedItem }"          

        addProduct="addProductToShoppingCart( event )"

        addProductToWishList="addProductToWishList( event )" />                   



<mx:TabNavigator width="100%" height="100%">



<!-- the Shopping Cart -->         

<cart:ShoppingCart

        id="shoppingCartComp"

        label="Shopping Cart"

        width="100%" height="100%"

        shoppingCart="{ ModelLocator.shoppingCart }"

        selectedItem="{ ModelLocator.selectedItem }"

        select="ModelLocator.selectedItem = event.target.selectedItem"

        currencyFormatter="{ ModelLocator.currencyFormatter }"

        addProduct="addProductToShoppingCart( event );"

        deleteProduct="deleteProductFromShoppingCart( event );"

        gotoCheckout="ModelLocator.workflowState =

                      ModelLocator.VIEWING_CHECKOUT;" />   



<!-- the Wish List -->

<cart:ShoppingCart

        id="wishListCartComp"

        label="Wish List"

        width="100%" height="100%"

        shoppingCart="{ ModelLocator.wishList }"

        selectedItem="{ ModelLocator.selectedItem }"

        select="ModelLocator.selectedItem = event.target.selectedItem"

        currencyFormatter="{ ModelLocator.currencyFormatter }"

        addProduct="addProductToWishList( event );"

        deleteProduct="deleteProductFromShoppingCart( event );"

        gotoCheckout="ModelLocator.workflowState =

                      ModelLocator.VIEWING_CHECKOUT;" />



</mx:TabNavigator>

ShoppingCart实例被在Tab Navigator 中的wishListCartComp调用。

首先,wishListCartComp实例数据绑定到ModelLocator.wishList,接着wishListModel上的任何改动都会自动影响Shopping Cart组件内部。这是acion中一个大组件重用的好例子。

addProduct事件调用了一个方法,addProductToWishList(),你需要在SideArea.mxml中定义,如下:

private function addProductToWishList( event : Object ) : Void

{      

EventBroadcaster.getInstance().broadcastEvent(

    ShopController.EVENT_ADD_PRODUCT_TO_WISH_LIST, event );

}



这个方法很好地将用户动作翻译成适当的Cairngorm事件;现在,当用户把产品放入ShoppingCart组件中的WishListCartComp实例时,被认为是添加到WishList的用户动作,这个动作被翻译成适当的Cairngorm事件。

我们在ProductDetails MXML组件中使用addProductToWishList()方法,如下使用:

addProductToWishList="addProductToWishList( event )"

很明显,这要求ProductDetails MXML组件能广播addProductToWishList事件效果就像是按下“添加到WishList”发生的事件一样。这样最后视图部分代码如ProductDetails.mxml

首先,在ProductDetails上更新MetaData定义要广播的新的事件,如下显示:

<mx:Metadata>

   [Event("addProduct")]

   [Event("addProductToWishList")]

</mx:Metadata>

最后,声明新的按钮,将它的点击事件变为更有意义的业务事件,addProductToWishList。声明一个有用的方法来广播addProductToWishList事件及附加的信息:产品被添加(当前选择的产品)和用户需要的数量:

private function addProductToWishList() : Void

{

var event : Object = new Object();

event.type = "addProductToWishList";

event.product = selectedItem;

event.quantity = quantity;

dispatchEvent( event );

}

你可以加入你一个新的按钮”添加到购物篮”来代替加入到需求列表,代码如下:

<mx:Button label="Add to Cart" click="addProduct();" />

<mx:Button label="Add to WishList" click="addProductToWishList();" />

这是一个很好的示例我们在第三章讨论过:把一个按钮的点击事件转变为一个更有意义的事件来表示我们的业务领域("addProductToWishList"),轻松地构建MXML组件。这样ProductDetails和SideArea MXML组件通过被广播,绑定不同的事件。

这就是它了。现在你已经加入了全部代码到MXML来表示用户操作需求列表的流程。通过动态绑定MXML到模型定位器类和将用户动作翻译成Cairngorm事件,视图被绑定在最底层的Cairngorm架构结构上。

你可能感兴趣的:(数据结构,应用服务器,Flex,领域模型,actionscript)