【永恒的瞬间】
☜Give me hapy ☞
架构一个Flex应用程序


使用Flex的web开发者可能最初会对用户界面模型感到困惑。虽然传统的、类似servlet的、请求-响应(request-response)模型将会在Flex中应用,但是却存在一种更好的方法。由于ActionScript语言中的“[Binding]”标签,你可以把你的视图绑定到模型数据,这样一来模型的更改就会自动影响到视图。Cairngorm 微型架构使这种方法得到形式化,而且它也是那些想要领会如何“让它们在一起工作”的开发者的一个非常好的起点。在这篇文章中我将描述变量绑定,特性驱动开发和Carigorm在NoteTag中是如何一起工作的

这里是一个典型的Flex应用程序可能的架构:


域(Domain)
·组成域模型的所有类。在NoteTag中,它包含了Notes(记录),Tasks(事务)以及Subscriptions(订阅)(Subscriptions是相关Notes或Tasks的一个群集(collection))。

模型(Model)
·一个保存域模型的可绑定实例的一个单体(singleton)。在NoteTag中,ModeLocator单体保存了用户的订阅清单,用户的连接,当前的订阅,当前的记录以及其他。

视图(View)
·UI组件(通常来说就是MXML文件,虽然并不总是)。依赖状态(state-dependent)的UI组件被绑定到ModeLocator的实例变量。如果ModeLocator中数据被标记为“[Bindable]”,那么它的任何改变都会导致UI自动更新。NoteTag中的一个例子就是NoteListView,它显示了当前订阅中的记录列表。如果当前的订阅或者它的任何一个记录改变了,那么NoteListView将会自动更新来反应这些改变。

控制器(Controller)
·同事件驱动的Commands一样执行特性所需的下部构造。NoteTag中的例子包括GetSubscriptionCommand, GetNoteCommand 以及其他。

业务(Business)
·完成域中对象操作的业务逻辑类,经常呼叫远程服务并且异步返回结果。对大部分NoteTag的业务逻辑来说SubscriptionManager类是entry point。

服务(Service)
·服务层,保存了用来呼叫远程服务(HTTPService,RemoteService和WebService)的所有类。NoteTag 使用了一个服务工厂(factory)类集合,减轻了对特殊的HTTP服务的部署,这些HTTP服务来自进行HTTP 服务呼叫的组件。

多数应用程序特性都有上面的一些或者全部结构。下面是一个典型特性的工作流程:
1.视图(view)广播一个事件。
2.单体控制器(controller)收到这个事件,把它映射到相应的Command,并且执行这个Command。
3.Command委托适当的业务(Business)对象执行业务逻辑。
4.业务(Business)对象执行业务逻辑,可能对不同的Service进行一个或者多个异步呼叫,并且通过分派(dispatch)一个新的事件给Command来返回结果。
5.Command将结果赋给单体模型。
6.绑定到单体模型中的数据的所有视图都自动更新。

它是如何在一个特定的特性中工作的呢?当用户从Notes列表(看下面,在屏幕的顶端)中选中了一个Note时,这个Note就会从适当的存储(Blogger或TypePad)中加载并显示在编辑器中(看下面,在屏幕的底端):


1.广播事件

当用户在第一个Note上点击时,NoteListView分派一个事件,如下:
程序代码 程序代码
// NoteListView.mxml
private function getSelectedEntry(event:ListEvent) : void
{
  var selectedEntry:TagBasedEntry =
    TagBasedEntry(currentFeed.entries[event.rowIndex-1]);
  Application.application.dispatchEvent(
    new GetNoteEvent(selectedEntry.metadata,true));
}


2. 对事件相应

因为NoteTag的Front Controller已经把它自身注册来监听所有的Command Event,当GetNoteEvent 被分派的时候它将会被通知。
程序代码 程序代码
// NoteTagController.as
public class NoteTagController extends FrontController
{
  public function NoteTagController()
  {
    addCommand(LoginEvent.EVENT_LOGIN, LoginCommand);
    addCommand(GetNoteEvent.EVENT_GET_NOTE, GetNoteCommand);
    addCommand(GetTaskEvent.EVENT_GET_TASK, GetTaskCommand);
    addCommand(PostNoteEvent.EVENT_POST_NOTE, PostNoteCommand);
    // more commands...
  }
}


Cairngorm的 Front Controller通过执行适当的command提供了监听事件和响应事件的基础构造。

3. 执行 Command

为了获得这个Note,NoteTag需要呼叫存储了用户note的blog服务器。实现了Cairngorm的Command接口的GetNoteCommand就像这样:

程序代码 程序代码
// GetNoteCommand.as
internal class GetNoteCommand extends ChainedCommand
{
  public override function execute(event:CairngormEvent):void
  {
    var initialEvent:GetNoteEvent = GetNoteEvent(event);

    var subscriptionManager:SubscriptionManager =
      ModelLocator.getInstance().subscriptionManager;

    setNextEventHandler(subscriptionManager,
      handleLoadNote,
      LoadNoteEvent.EVENT_LOAD_NOTE,
      onSubscriptionFault,
      SubscriptionFaultEvent.EVENT_SUBSCRIPTION_FAULT);
    subscriptionManager.loadNote(initialEvent.metadata);
  }

  private function handleLoadNote(event:LoadNoteEvent):void
  {
    // handle result here...
  }

  // ...
}


(你可能已经发现了GetNoteCommand 继承了 ChainedCommand——这个类使用setNextEventHandler 方法共同进行一系列异步呼叫。在以后的文章里,我将更深入地讨论ChainedCommand,并大体说一下异步回应器(responder)。)

4. 执行业务逻辑

SubscriptionManager通过执行对tag服务器和blog服务器一系列的HTTP service呼叫来触发Note的加载。当note被加载完毕时,SubscriptionManager 将分派一个LoadNoteEvent,这个事件将会被GetNoteCommand.handleLoadNote(参见下一条)触发。

5. 更新模型

GetNoteCommand 通过把加载的Note赋给ModelLocator中适当的数据成员来响应事件。
程序代码 程序代码
// GetNoteCommand.as
internal class GetNoteCommand extends ChainedCommand
{
  // ...

  private function handleLoadNote(event:LoadNoteEvent):void
  {
    ModelLocator.getInstance().currentNote = event.note;
  }

  // ...
}


6. 更新视图

任何绑定到ModelLocator的currentNote成员的视图都将会自动更新来反映新的数据。NoteView,负责在编辑器中显示Note的组件,是这样的一个视图:
程序代码 程序代码
// NoteView.mxml
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:view="com.adobe.kiwi.notetag.view.*"
    xmlns="*">
  <mx:Script>
  <![CDATA[
    import com.adobe.kiwi.notetag.model.*;
    [Bindable] private var model:ModelLocator = ModelLocator.getInstance();
  ]]>
  </mx:Script>

  <view:NoteEdit id="noteEditor" width="100%" height="100%"
    note="{model.currentNote}" />

</mx:VBox>


所有的其它特性——发布一个Note,获得一个Subscription,更新一个Task——会通过同样的Event-to-Command-to-Model-to-View途径来实现。Command-specific事件可以从多种内容中被分派,,而不需要知道哪一个视图会被影响。视图可以绑定到模型的变化而并不需要知道引发的事件是从什么地方被分派的。由于松耦合,出现了更清洁,更容易维护的代码。
posted on 2007-02-28 16:55 ☜♥☞MengChuChen 阅读(381) 评论(0)  编辑  收藏 所属分类: flex2Cairngorm

只有注册用户登录后才能发表评论。


网站导航: