Robotlegs AS3入门介绍--第一部分:Context(上下文)与Mediators(媒介)

来源:互联网 发布:linux 有道云笔记 编辑:程序博客网 时间:2024/06/06 10:42

这是由RIAMeeting翻译小组出品的翻译文章,转载请注明出处。

英文原文地址:http://insideria.com/2010/06/an-introduction-to-robotlegs-a.html

目前,我们了解到有相当多的途径,可以用来在Actionscript 3的开发环境中构建框架,这是一个很好的迹象。可见开源社区是一个充满活力,富有创造性的地方,那些令开发工作变得更加简单的工具都是很好用的东西。经历了过去一年的发展,Robotlegs AS3得到了快速的推广和广泛的采用。目前,Robotlegs AS3已经广泛应用在主流媒体,创业公司以及各种规模的企业之中。 
b
本文是InsideRIA社区中首发的关于Robotlegs 的系列文章之一。 在接下来的几个星期,将会有更多本系列的文章出炉,来详细介绍Robotlegs的核心概念以及一些更加高级的概念,包括第三方程序的使用以及使其相互作用的程序库。

何谓Robotlegs?

简单地来说,Robotlegs是一套机制,用来将您所创建的对象联接在一起。 比如说,对象A要传达信息给对象B。 然而,对象A不想或者说不需要知道对象B是否存在。 这样子两个对象之间如何进行信息交流呢? 一个简单的方法是通过事件(Events)来处理。 在Flash当中,我们有一个本地事件系统,用于促进这种类型的信息交流。 就像您平时应用Event(事件)那样。 显示列表中的对象通过事件进行信息沟通,事件冒泡使得远处的对象可以从其它显示对象中接收信息。 而对于那些没有在显示列表上的对象,该如何处理这一问题呢? 这时,像Robotlegs 这样的框架就可以很好地解决这个问题了。

Robotlegs的核心是一组模块化的工具集以及界面集,用来简化上述的信息沟通任务,减少重复的代码编写,以及管理应用程序中的依赖注入。除了这些核心工具集以外,Robotlegs还提供了一个比较规范的MVC+S(Model模型,View视图,Controller控件和Service服务)实现,来帮助您开展工作。如果您对PureMVC有经验的话,相信您也会很快学会使用Robotlegs MVC+S实现中的媒介(Mediators)和命令(Commands)。如果对PureMVC没有经验,也不必担心,在本课程中我们将会更深入地探讨这个问题。

本文将会通过一个简单的“Hello World‘例子,对Robotlegs的功能进行概述。 当您看到这个例子的时候可能会说:“呃?我可以在单个的MXML文件中就能实现这个功能,何苦这么麻烦呢!‘ 对于这个微不足道的小例子来说,确实没必要这么麻烦,但是请注意,对于一个庞大的项目来说,这种结构的优越性将是无可比拟的。 总之,这是利用框架进行开发的好处。 相比起那些没有模式,不经实践,只是随意堆叠在一起的应用程序来说,这种方法可以让我们更有效,更快速地沟通概念以及理解代码库。

本文将不会对Robotlegs作过分详尽的探究,但我希望这些内容足以引起您的兴趣。 在文章的末尾,我粘贴了一些资源的地址,用以对Robotlegs作更进一步的探究。 接下来,我们来看一些代码的应用!

Robotlegs MVC+S 应用程序的基本结构

一个典型的Robotlegs MVC+S应用程序包含以下几个部分:

  • Context(上下文) - 所谓Context(上下文),实际上是一套自展机制,用来初始化Robotlegs所使用的依赖注入以及各种核心工具。
  • Mediators(媒介) - 所谓Mediators(媒介),是用来管理应用程序中的视图组件与应用程序中的其它对象之间的信息交流。
  • Commands(命令) - 所谓Commands(命令) ,代表的是应用程序所能执行的独立操作。 通常,Commands(命令)会作为对用户操作的反应,但命令的作用并不仅限于此。
  • Models(模型) - Models(模型)中保存着数据信息,并且表现出应用程序当前的状态。
  • Services(服务) - Services(服务),是应用程序与外界的接口。

我们现在来进一步地探讨一下Context(上下文)和Mediators(媒介),首先开始的是Context(上下文))。

Context处于应用程序的核心位置。 它提供了中央事件总线,使得您其它的应用程序对象之间可以进行信息交流。 比起应用程序初始化加载以及程序引导来说更优越的一点是,您在常规的开发工作中不需要去理会Context。 它会自发启动,执行自己的功能,并且,伴随者您开发出了程序的核心,它又会悄无声息地自动退出。 此外,Context并不是一个单例。 应用程序中可以存在自定义数量的Contexts,使得Robotlegs很适合用来开发模块化的应用程序。 我们不打算在这篇文章中深究模块化的应用程序,但是它将会是另一篇即将问世的文章的主题,因为模块化应用程序实在是一款极为强大的工具。 作为开始,我们先来看一下最基本的Context。

HelloWorldContext.as

  1. import org.robotlegs.mvcs.Context
  2. public class HelloWorldContext extends Context
  3. {
  4. override public function startup():void
  5.      {
  6.           //这个是引导程序
  7.       }
  8. }
复制代码

Context当中的一些内容实际上让您重写了程序启动类函数。 当Context完全初始化以后,会调用startup()函数。 在程序后台,早在调用startup()函数之前,Context就已经创建了全部的Robotlegs核心工具,为接收依赖注入的变换映像作准备,同时还创建了事件调度器,用于应用程序中的各个对象之间的信息交流。

一旦您创建了一个Context类,您的程序就需要对它进行引用。 在Flex 4 Spark 程序中,这一步通常会在作扩展程序用的主MXML文件中进行,如下所示。

HelloWorld.mxml

  1. <?xml version="1.0"?>
  2. <s:Application
  3. xmlns:fx="http://ns.adobe.com/mxml/2009"
  4. xmlns:s="library://ns.adobe.com/flex/spark"
  5. xmlns:mx="library://ns.adobe.com/flex/mx"
  6. xmlns:local="*">
  7.     <fx:Declarations>
  8. <local:HelloWorldContext contextView="{this}"/>
  9.     </fx:Declarations>
  10. </s:Application>
复制代码

因为Context是一个不可视的类,所以它必须要放置在Declarations 标签中。 您还需要注意到,contextView property(Context视图属性)受限于应用程序本身。 context view(context 视图)是Context的根视图,结合view component mediation(视图组件媒介)为开发者提供自动化协助。 当在后面讨论到views(视图) 和 mediators(媒介)的关系时,我们会对context view(context 视图)进行简单的介绍。

以上是对Context的介绍。 正如前面所提到的,Context在应用程序生命周期中的作用时间非常短暂,但却处于决定性的地位。 准备好您的Context,我们现在可以加入一些视图组件并通过Robotlegs使他们相互间进行信息交流。 现在让我们来关注一下Mediators(媒介)以及它们与应用程序的视图组件之间的关系。

Mediators(媒介)处于视图组件和程序的其他部分之间。 简单地来说,Mediators会对事件进行监听。 当用户与视图组件进行交互或者视图组件经由其他一些方式进行更新时,这些视图组件都会分派出一些事件。 这些事件需要被程序的其它部分获取,并再次进行传递。 或许是用户点击了保存按钮,因此一些信息需要被传递到服务器中去。 而mediator正监听着这一事件,一旦这个事件发生,mediator就会收集适用的信息,并发送出一个事件,程序的其它部分可以根据这个事件来对数据执行某些特定的操作。 同样地,Mediator也会监听来自程序其它部分发送来的事件。 如果从服务器接收到一些被解析的数据,以及一个事件正被一个service类分派出来,那么Mediator就是您监听这个事件以及用新数据更新这个事件的视图组件的地方。 下面来看一下怎样接收一个Mediator。

MessageView.mxml

  1. <?xml version="1.0"?>
  2. <s:TextArea
  3. xmlns:fx="http://ns.adobe.com/mxml/2009"
  4. xmlns:s="library://ns.adobe.com/flex/spark"
  5. xmlns:mx="library://ns.adobe.com/flex/mx">
  6. </s:TextArea>
复制代码

这个类的内容非常简短。 仅仅是TextArea的一个简单的扩展。 那为什么不简单地用一个TextArea来实现呢? 依赖注入对于明确的类的作用更明显。 这句话的意思是说,通过扩展TextArea到新建的MessageView类之中,我们同时创建了一个特定的视图组件,使得依赖注入会依照这个视图组件来起作用。 如果我们的程序具有多个目的不同的TextAreas,那么这一点将是相当重要的。 通过这种方式来对类进行划分,我们可以很明确地定义类的意图,并且令到依赖注入工具可以有效地工作。 利用MessageView,我们以后还可以加入一些额外的功能。 拿这个例子来说,它仍然会是一个简单的TextArea,但您一定已经明白这个概念了。 现在,我们来看一下用于MessageView组件的mediator。

MessageViewMediator.as

import org.robotlegs.mvcs.Mediator;
  1. import org.robotlegs.mvcs.Mediator;
  2. public class MessageViewMediator extends Mediator
  3. {
  4.      [Inject]
  5.      public var view:MessageView;
  6. override public function onRegister():void
  7.      {
  8.            trace("I am registered!");
  9.       }
  10. }
复制代码

在这里可以看到,MessageViewMediator有两个有趣的特征。 您马上就察觉到了,我们首次使用了[Inject]元数据标签。 这个标签用于让Robotlegs识别那些需要执行注入操作的属性和类函数。 利用mediator的作用,当创建了一个mediator之后,对于注入操作来说总会有一个可用的mediated view(媒介视图)。 因此,您不需要对绘制注入操作的视图进行任何特殊的考虑。 当您为它的mediator绘制视图的时候,这一点已经被自动地考虑到了。 稍后,我们会具体地看一下上面所说的那一个特征。但现在我们先来看一下基本mediator的另外一个有趣的特征,这个特征体现在onRegister()函数中。

函数onRegister()是一个钩子,会在mediator完全初始化之后执行。 当注入操作完成,视图也准备好了,通常这里就是您为视图组件和程序添加事件监听器的地方。 一般来说,您会在每个您所创建的Robotlegs mediator中重写这个函数。

既然您已经得到了一个视图组件以及一个mediator,它们需要用Context来进行注册和定义映射。 这一步操作是通过MediatorMap来实现的。 正如其名,MediatorMap是一个将mediators映射到Context的视图组件中去的工具。 此外,默认状态下,MediatorMap 会监听ADDED_TO_STAGE事件以及REMOVED_FROM_STAGE事件的发生,以此判断是否会随着显示列表中视图组件的添加或者移除,来自动创建或者删除相应的mediators。 其实这并没有多大价值,对于画质精细的程序来说,显示对象的数量非常庞大(1000s),这个自动化媒介处理可能会引起一些性能问题。 当然,在一个典型的程序里,这还是非常方便的工具,并且也极少会引起性能问题。 以下代码展示了我们如何将MessageView 映射到HelloWorldContext的MessageViewMediator 中去。

  1. override public function startup():void
  2. {
  3.      mediatorMap.mapView(MessageView, MessageViewMediator);
  4. }
复制代码

这一步完成之后,剩下的全部工作就是要把MessageView 添加到HelloWorld.mxml中去了。

HelloWorld.mxml

  1. <?xml version="1.0"?>
  2. <s:Application
  3. xmlns:fx="http://ns.adobe.com/mxml/2009"
  4. xmlns:s="library://ns.adobe.com/flex/spark"
  5. xmlns:mx="library://ns.adobe.com/flex/mx"
  6. xmlns:local="*">
  7.     <fx:Declarations>
  8. <local:HelloWorldContext contextView="{this}"/>
  9.     </fx:Declarations>
  10. <local:MessageView top="40" width="100%" height="100%"/>
  11. </s:Application>
复制代码

现在,当在调试器中运行您的程序的时候,您将会看到控制台上显示“我已经注册了!‘ 祝贺您,您已经拥有一个功能完整的Robotlegs程序了。 但是,这个功能现在几乎是没有限制的,不过我们可以改变这一点。 让我们给这个程序添加一些除了自展以外的其它东西吧。下面,我们会添加一个称为HelloButton 的按钮,这仅仅是Spark Button类的一个扩展,同时会为HelloButton添加一个mediator,名为...... HelloButtonMediator。

HelloButton.mxml

  1. <?xml version="1.0"?>
  2. <s:Button
  3. xmlns:fx="http://ns.adobe.com/mxml/2009"
  4. xmlns:s="library://ns.adobe.com/flex/spark"
  5. xmlns:mx="library://ns.adobe.com/flex/mx">
  6. </s:Button>
复制代码

HelloButtonMediator.as

  1. import flash.events.MouseEvent;
  2. import org.robotlegs.mvcs.Mediator;
  3. public class HelloButtonMediator extends Mediator
  4. {
  5.      [Inject]
  6.      public var view:HelloButton;
  7. override public function onRegister():void
  8.      {
  9.  
  10.       }
  11. }
复制代码

现在,除了类不相同以外,这个HelloButtonMediator看起来就跟上面说到的那个MessageViewMediator一样。 一旦您为HelloButton 以及它的mediator添加映射以后,您的Context启动函数看起来也会跟上面的一样。

  1. override public function startup():void
  2. {
  3.      mediatorMap.mapView(MessageView, MessageViewMediator);
  4.      mediatorMap.mapView(HelloButton, HelloButtonMediator);
  5. }
复制代码

同样,您可以往HelloWorld.mxml之中添加这个按钮,使其加入到显示列表中去。 您可能还希望把其它一些有趣的东西添加到HelloButton的label property(标签属性)之中,这些操作就留待您自己来完成咯。

HelloWorld.mxml

普通浏览复制代码
  1. <?xml version="1.0"?>
  2. <s:Application
  3. xmlns:fx="http://ns.adobe.com/mxml/2009"
  4. xmlns:s="library://ns.adobe.com/flex/spark"
  5. xmlns:mx="library://ns.adobe.com/flex/mx"
  6. xmlns:local="*">
  7.     <fx:Declarations>
  8. <local:HelloWorldContext contextView="{this}"/>
  9.     </fx:Declarations>
  10. <local:HelloButton label="Say Hello"/>
  11. <local:MessageView top="40" width="100%" height="100%"/>
  12. </s:Application>

到目前为止,我们有两个完全mediated(调解)的视图组件了,它们已经迫不及待想进行信息交流了。 我并不是一个会拒绝自己对象渴求的人,所以,就让它们交流去吧;首先需要创建一个用于信息交流的自定义事件。

HelloWorldMessageEvent.as

普通浏览复制代码
  1. public class HelloWorldMessageEvent extends Event
  2. {
  3.      static const MESSAGE_DISPATCHED:String = "messageDispatched";
  4.      
  5.      private var _message:String;
  6.      public function get message():String
  7.      {
  8.           return _message;
  9.       }
  10.      public function HelloWorldMessageEvent(type:String, message:String, bubbles:Boolean = false, cancelable:Boolean = false)
  11.      {
  12.           super(type, bubbles, cancelable);
  13. _message = message;
  14.       }
  15. override public function clone():Event
  16.      {
  17.           return new HelloWorldMessageEvent(type, message, bubbles, cancelable)
  18.       }
  19. }

这是一个简单的自定义事件。 请确保重写自定义事件中的clone()函数。 因为如果没有这个函数的话,事件将不能被重复分派、传递或者冒泡。 对此我已经养成了习惯,对于所有自定义事件,我都会重写其中的clone()函数。 当经历过数小时烧坏脑的调试工作之后,相信您也会养成这个习惯。

我们想实现的功能是,当用户点击HelloButton的时候,MessageView会进行更新。 因此,HelloButtonMediator需要监听发生在HelloButton上的MouseEvent.CLICK事件,然后将HelloWorldMessageEvent事件分发到程序之中。 我们并不知道哪部分对这个事件作出了响应。 并且也没必要知道哪个部分对这个事件作出响应。 反正HelloButtonMediator会处理好这一切。

HelloButtonMediator.as

普通浏览复制代码
  1. public class HelloButtonMediator extends Mediator
  2. {
  3.      [Inject]
  4.      public var view:HelloButton;
  5. override public function onRegister():void
  6.      {
  7.           addViewListener(MouseEvent.CLICK, handleMouseClick)
  8.       }
  9.      private function handleMouseClick(event:MouseEvent):void
  10.      {
  11. dispatch(new HelloWorldMessageEvent(HelloWorldMessageEvent.MESSAGE_DISPATCHED, "Hello World"));
  12.       }
  13. }

随着视图监听器添加到HelloButtonMediator中来,我们现在会分派一个事件到应用程序之中。 下一步,就是要对事件作一些处理了。 应用MessageViewMediator 似乎是一个合乎逻辑的选择。

MessageViewMediator.as

普通浏览复制代码
  1. public class MessageViewMediator extends Mediator
  2. {
  3.      [Inject]
  4.      public var view:MessageView;
  5. override public function onRegister():void
  6.      {
  7.           addContextListener(HelloWorldMessageEvent.MESSAGE_DISPATCHED, handleMessage)
  8.       }
  9.      private function handleMessage(event:HelloWorldMessageEvent):void
  10.      {
  11. view.text = event.message;
  12.       }
  13. }

通过以上介绍,我们已经完成了这个Hello World的例子(原文相关语,大意为初步认识了Robotlegs的一些方面)!貌似这个认识过程稍微有点长,但是在您编写重要程序的时候,就会觉得付出的这些时间是值得的。在本系列文章的下一部分,我们将会探索一下models以及commands的应用,紧接着,我们还会学习一下services。除了Robotlegs的核心内容以外,我们还会在一些功能上深究,当我们使用AS3-Signals以及模块化程序开发等工具的时候,这些功能都派得上用场。应用Robotlegs来进行开发是相当令人兴奋的。我相当期待,在接下来几周的时间里能通过InsideRIA这个平台与大家分享我在这方面的一些热望。

如果您实在迫不及待了,我的博客里(以及许多其他网络资源中)有一些文章也是涉及到许多Robotlegs主题的。John Lindquist在他的博客上也放置了一段关于Hello World的截屏视频(观看他如何使用FDT本身来说也是非常有趣的)。此外,还有一个叫Best Practices(最佳实践)的文件,很多人认为这个文件对于学习Robotlegs是有很大帮助的。您还可以经常访问Robotlegs Knowledge Base(Robotlegs 知识库)寻求帮助和支持。那里有许多积极的社区志愿者,包括我自己在内,会很勤快地回答各种相关于Robotlegs 的疑问。除了以上所说的以外,我参与到一本名为Flex 4 in Action的图书编辑工作之中,这本书即将面世,当中有22页的篇幅是Robotlegs 的材料。


转载:http://www.riadev.com/flex-thread-262-1-1.html