Robotlegs AS3第二部分:模型入门介绍

Adobe Flash Builder 4 简体中文正式版 Windows版点击下载:http://g.csdn.net/5134151

Adobe Flash Builder 4 简体中文正式版 Mac版点击下载http://g.csdn.net/5134152

Adobe 在线课堂:http://adobev.csdn.net/zx/index.html
Adobe平台技术峰会课程视频:http://adobev.csdn.net/

这是InsideRIA 上关于R   obotlegs AS3 入门介绍系列文章的第二部分。在第一部分,我们了解了何为Robotlegs AS3 ,并且简要介绍了RobotlegsContext (上下文)与Mediators (媒介)。本文将继续阐述这些概念,并介绍相关模型。

 

如果你错过了第一部分,请看Robotlegs AS3 入门介绍-- 第一部分:Context (上下文)与Mediators (媒介)(Part One: Context and Mediators )。

 

模型是什么?

模型类封装你的应用程序数据,提供API 读取和操作数据。你的程序中的其它类会通过该API 请求模型。当模型中的数据更新后,模型会派遣事件,而你的程序中的其它类会对这些事件予以反应。模型适合于抓取域逻辑,如执行计算或其它操作。有个例子就是购物车。向购物车模型添加了一个条目之后,购物车内新的商品总量会再次统计。新的总量被存储在模型里,供程序中的其它类读取。将这个逻辑置入你的模型里,确保不会“散落”在整个程序里,你就可以确切地知道哪里去查看你的数据是怎么样,在何时被操作了。

除了控制数据的读取外,模型还将保持程序的状态(state) 。状态(State ),就数据模型这个意义来说,和Flex State 并非同一个概念,这与控制程序的外观有关。当然,它们也是相关的,但是有了这个模型,就要考虑一系列对象。你想要跟踪选定的目标,所以数据模型有一个选择属性,随时根据选定的条目更新。程序的其它领域现在可以读取这个属性,查看哪个条目被选定,然后做出相应的反应。

 

模型可编写成便携式的。就如同你要牢记Services (服务)一样,在开发模型时,你也要记住这点。有许多常见的数据组,可以轻松在一个程序和下一个之间传输。譬如,UserLoginModel 或者ShoppingCartModel 。让模型具有编写性,也不过是多花费些时间和精力,但是也就像为每个项目重复编写一次代码差不多。

 

模型的确值得你更多的注意力。它是你的程序的核心。大家总是惊叹于这些视觉组件。但是作为一个开发人员,你知道,数据才是真正最为重要的。这就是我们的任务,作为开发人员的任务:创建数据,精确传送到那些美丽的界面。这就是为什么在模型中隔离域逻辑非常重要。通过隔离,你就可以更轻松地定位,更新和维护数据了。对模型进行了基本的定义后,我们来看看一个小例子。

 

看看代码!

这个纯粹的 AS3 程序利用的是Keith Peters 出色的Minimal Comps 库。不要担心,如果你喜欢Flex ( 你怎么会不喜欢呢?) , 下一个例子就是一个Flex 程序。不过有了Minimal Comps 库,就可以很快地使用AS3 举出例子;而RobotlegsFlex 一样,使用起来很轻松。所以,我们来看看程序的具体内容吧。

SimpleListExample.zip

 

上面是一个我们将要检查的程序。这是一个简单的作者列表,选定的作者会有一段引言展示。这是一个归档的Flash Builder 项目 (zip 168k), 包括Robotlegs 1.1 MinimalComps SWC 文件。

 

SimpleListExample.as

public class SimpleListExample extends Sprite

{

   public function SimpleListExample()

   {

      stage. align = StageAlign.TOP_LEFT;

      stage. scaleMode = StageScaleMode.NO_SCALE;

    }

}

 

这是程序的主要文件,进入点。这是个简单的Sprite 。是Robotlegs 程序。所以我们需要做的第一件事就是创建一个Context: (上下文)。

 

SimpleListExampleContext.as

public class SimpleListExampleContext extends Context

{

     public function SimpleListExampleContext(contextView:DisplayObjectContainer)

     {

          super (contextView);

      }

 

     override public function startup(): void

     {

          injector.mapSingleton(AuthorModel);

         

          mediatorMap.mapView(ListView, ListViewMediator);

          mediatorMap.mapView(QuoteTextArea, QuoteTextAreaMediator);

         

          mediatorMap.mapView(SimpleListExample, ApplicationMediator);

      }

}

 

Flex 程序中,通常会完全省略构造函数,因为在MXML 声明内,设定了contextView 。在Actionscript 程序中,你将contextView 传入构造函数中,这样可以立即设定。现在,我们在主application view 中创建一个SimpleListExampleContext 的实例。

SimpleListExample.as

public class SimpleListExample extends Sprite

{

   public var context:SimpleListExampleContext;

 

   public function SimpleListExample()

   {

      stage. align = StageAlign.TOP_LEFT;

      stage. scaleMode = StageScaleMode.NO_SCALE;

      context = new SimpleListExampleContext( this );

    }

}

你应该注意到上下文变量。必须有一个上下文的参考,这点很重要。如果你只是在构造函数里创建一个SimpleListExampleContext 的新实例,而不将它放入变量,那它就是一个收集在Flash Players 上的无用“垃圾”。这会导致一些麻烦,需要花费时间认真处理!有了Flex, 你只需在MXML (紧跟参照,而无需变量)中声明上下文,除非你在Script tag 中创建了上下文。它将要求一个参照,正如上面给出的Actionscript 范例。

 

程序有两个视图。可以选定一个名称表,而文本区域会显示列表中选定条目的quote 。这些视图为:

l  ListView

l  QuoteTextArea

命名方案倒还有创意。这两个视图就是基础Minimal Comps 类的次类。它们是:

ListView.as

public class ListView extends List

{

   public function ListView(parent:DisplayObjectContainer)

   {

      super (parent);

    }

}

QuoteTextArea.as

public class QuoteTextArea extends TextArea

{

   public function QuoteTextArea(parent:DisplayObjectContainer)

   {

      super (parent);

    }

}

 

这个程序只有其中的一个。如果我们只是使用基础列表和TextArea 类,功能性就非常强。为什么都是使用次类?这是一个好习惯,尤其当处理依赖注入时。如果我们知道,我们将要协调一个视图类,将它加入次类,然后根据其目的进行命名。这么做时,很容易就隔离开,以进行协调。

 

现在,我们需要做的就是,将这两个视图作为主程序的子程序放入stage 上。在

Actionscript 程序中,我倾向于将createChildren 方法放在主视图上,然后从mediator 中调用。下面是才采用createChildren 方法的主视图。

 

SimpleListExample.as

 

public class SimpleListExample extends Sprite

{

     private var hbox:HBox;

     private var list :ListView;

     private var quoteText:QuoteTextArea;

 

     public var context:SimpleListExampleContext;

 

     public function SimpleListExample()

     {

          stage. align = StageAlign.TOP_LEFT;

          stage. scaleMode = StageScaleMode.NO_SCALE;

          context = new SimpleListExampleContext( this );

      }

 

     /**

      * Called from ApplicationMediator's onRegister()

      */

     public function createChildren(): void

     {

          hbox = new HBox( this ,0,0);

          addChild(hbox);

 

          list = new ListView(hbox);

          list .alternateRows = true ;

 

          quoteText = new QuoteTextArea(hbox);

          quoteText.editable = false ;

          quoteText. selectable = false ;

      }

}

 

 

你可能会想,为什么不直接在构造函数中添加视图?我倾向于在添加子程序之前,启动Robotlegs 。从ApplicationMediator 中调用createChildren() ,我们可以百分百地确定所有的初始应用bootstrapping 已经完成,准备完毕。下面是ApplicationMediator:

 

ApplicationMediator.as

public class ApplicationMediator extends Mediator

{

   [Inject]

   public var view:SimpleListExample;

 

   override public function onRegister(): void

   {

      view.createChildren();

    }

}

 

在本例中,mediator 只有一个任务,但是在你的程序中,协调视图(contextView )是个轻便的机制。Mediator 被创建之后,需要被映射。这一步很简单,也容易被遗漏,但是当你遇到bug ,而你的mediator 不予反应时,你就会想起来。很有可能发生的情况就是;它没有被映射,或者它的视图组件不是ADDED_TO_STAGE 。在你的上下文中,启动的方法如下所示:

 

SimpleListExampleContext.as

override public function startup(): void

{  

     mediatorMap.mapView(SimpleListExample, ApplicationMediator);

}

当你现在运行程序时,你应该看到一个空列表和文本区域。现在,我们需要获取一些数据视图。我们将在模型类里放入一些静态数据,而我们的视图组件的mediator 可以读取。下面是基本的模型:

AuthorModel.as

 

public class AuthorModel extends Actor

{

   private var _list: Array ;

 

   public function get list (): Array

   {

        if (!_list)

            initializeList();

        return _list;

    }

 

   protected function initializeList(): void

   {

      var twain:Author = new Author(" Twain ");

      var poe:Author = new Author(" Poe ");

      var plato:Author = new Author(" Plato ");

      var fowler:Author = new Author(" Fowler ");

 

      twain.quote = " Why, I have known clergymen, good men, kind-hearted, liberal, sincere " +

              " , and all that, who did not know the meaning of a 'flush.' It is enough " +

              " to make one ashamed of one's species. ";

      fowler.quote = " Any fool can write code that a computer can understand. " +

              " Good programmers write code that humans can understand. ";

      poe.quote = " Deep into that darkness peering, long I stood there, wondering, " +

              " fearing, doubting, dreaming dreams no mortal ever dared to dream before. ";

      plato.quote = " All things will be produced in superior quantity and quality, and with greater ease, " +

              " when each man works at a single occupation, in accordance with his natural gifts, " +

              " and at the right moment, without meddling with anything else. ";

 

      _list = [twain,fowler,poe,plato];

    }

}

你马上会注意到AuthorModel 拓展 ActorActor 是一个配有MVCS 的便捷类。它提供了恰当的代码,用于注入用来在上下文中沟通的IEventDispatcher ,及通过该调度程序发送时间的dispatch() 方法。不一定要拓展Actor, 但是如果不拓展的话,你就需要亲自提供IEventDispatcher 注入点。Robotlegs MVC+S 中的模型是纯粹概念性的。没有模型类可拓展,命名规则不过是用来表述类的意图。

 

模型现在提供一个我欣赏的作者列表,并附有每位作者的一段引言。从下面可以看出,作者类是一个可以保存这些属性的简单的价值对象:

Author.as

public class Author

{

   public var name : String ;

   public var quote: String ;

 

   public function Author( name : String )

   {

      this . name = name ;

    }

 

          /**

            * Minimal comps took issue with toString();

            * @return

            *

            */      

          public function get label (): String

          {

                 return name ;

         }

}

当模型中充满数据时,我们需要向列表传递数据,以供展示。第一步就是,映射模型,进行注入。第二步就是协调ListView ,将数据从模型转移到列表。你的上下文的startup() 方法,在模型映射之后,会如下所示;

SimpleListExampleContext.as

override public function startup(): void

{

   injector.mapSingleton(AuthorModel);

  

   mediatorMap.mapView(SimpleListExample, ApplicationMediator);

}

为了映射模型,或者其它你想要诸如的类,你将使用上下文的注入器。注入器有几个方法用于映射类和值。mapSingleton() 方法是常用来为注入而映射简单类的方法。应该注意到,在该上下文中,“singleton ”不是指的设计模式中严格意义上的Singleton 。在本例中,mapSingleton() 意味着只要有要求,注入器将创建,并提供AuthorModel 的单一实例。如果你想要的话,你可以有任意个AuthorModel 实例,但是注入器只会创建,并注入你使用上述映射要求的那一个。还有其它几个方法可使用注入器进行映射,我强烈推荐您读一读Till Schneideriet 关于SwiftSuspenders Injector 的文章。在缺省状态下,这就是Robotlegs 使用的。Robotlegs 最佳实践(Robotlegs Best Practices )也讨论了使用注入器。此外,我们还将讨论其它注入映射选择。

 

有了用于注入的,已被映射的模型,我们就可以进一步将作者放入他们的列表。为此,我们将协调ListView ,将 AuthorModel 注入 mediator 中。下面是ListViewmediator

ListViewMediator.as

public class ListViewMediator extends Mediator

{

   [Inject]

   public var view:ListView;

 

   [Inject]

   public var authorModel:AuthorModel;

 

   override public function onRegister(): void

   {

      view.items = authorModel. list ;

    }

}

在协调的视图组件 [Inject] 标签外,我们没有看[Inject] 标签的使用,也没有讨论Robotlegs 如何提供读取你的类的依赖注入方法。在ListViewMediator 中,你会看到视图被注入。除了视图外,还有一个公共属性,属于类型AuthorModelAuthorModel ,标识有[Inject] 标签。当你在公共属性上放置[Inject] 标签,Robotlegs 创建类时,它将根据你映射的规则“注入”该属性。注意,如果你没有为注入提供规则,你会收到一个运行时间错误,将你指向问题所在。如果这种情况发生,确保捡查映射。在本例中,我们还使用了mapSingleton() ,以让AuthorModel 为注入做好准备。

 

mediator 被完整地构架,并且提供注入,使用运行的mediatoronRegister() 方法,我们读取模型,并且将列表属性赋值到视图的条目属性。真棒!我们现在应该看看列表中的条目。

 

... ...

 

... 真棒!...

 

首先,我们需要映射mediator

SimpleListExampleContext.as

override public function startup(): void

{

   injector.mapSingleton(AuthorModel);

  

   mediatorMap.mapView(ListView, ListViewMediator);

  

   mediatorMap.mapView(SimpleListExample, ApplicationMediator);

}

 

这就对了。ListView 被协调。记住映射的次序。ListView mediator 映射在SimpleListExample ( 主图) 映射之上。SimpleListExample 是主图,正如 contextView 。当contextView 的类被映射时,它的处理略有不同。contextView 马上被协调,而不是监听ADDED_TO_STAGE 事件的发生。鉴于contextView 已经出现,我们不能坚持监听可能不会发生的事件。因为我们想要ListView 被添加时,进行协调,我们要确保它在ApplicationMediators onRegister() 有机会被调用前被映射。如果你再次调用,就是在ApplicationMediator's onRegister() 这里,我们将要通知主视图添加子集,包括 ListView

 


所以,当ListView 被填充满数据,你现在可以从列表中的作者列表中进行选择了。非常激动吧?稍微控制下自己吧。不过还有步骤要继续。现在,我们想要用一些文本填充QuoteTextArea ,最好是选定作者的引言。为此,我们将要向AuthorModel 做一些添加,以跟综选定的作者。当作者在ListView 内选定,ListViewMediator 将更新AuthorModelAuthorModel 然后发送事件,通知任何可能任何可能正在监听的人,作者被选定。我们还需要为QuoteTextArea 创建一个mediator ,这样就可以监听该事件,并且根据选定作者的引言予以更新。我们知道我们会需要该事件,所以首先;

SelectedAuthorEvent.as

public class SelectedAuthorEvent extends Event

{

   public static const SELECTED: String = " authorSelected ";

 

   private var _author:Author;

 

   public function get author():Author

   {

      return _author;

    }

 

   public function SelectedAuthorEvent( type : String , author:Author = null , bubbles: Boolean = false , cancelable: Boolean = false )

   {

      super ( type , bubbles, cancelable);

      _author = author;

    }

 

   override public function clone():Event

   {

      return new SelectedAuthorEvent( type , author, bubbles, cancelable)

    }

}

该事件并不引人注意。它就是个典型的定制事件,有一个常量,SELECTED ,用于可选择的作者参数。鉴于我们已经有了该事件,我们需要更新AuthorModel ,跟综选定的作者,当出现更改时,通知程序。

 

AuthorModel.as

private var _selected:Author;

public function get selected():Author

{

   return _selected;

}

 

public function set selected(value:Author): void

{

   _selected = value;

   dispatch( new SelectedAuthorEvent(SelectedAuthorEvent.SELECTED, selected));

}

有了添加到AuthorModel 的代码(如上所示),我们能够有效地跟综现在选定的作者。为了在该模型上设定该属性,我们将更新ListViewMediator ,为ListView 选定的事件进行监听,并适时更新模型。

 

ListViewMediator.as

override public function onRegister(): void

{

   view.items = authorModel. list ;

  

   addViewListener(Event.SELECT, handleSelected)

}

 

private function handleSelected(event:Event): void

{

    authorModel.selected = view.selectedItem as Author;

}

 

ListViewMediator's onRegister() 里面,我们将使用addViewListener() 方法,在视图上添加一个事件监听器,使用handleSelected 方法处理。现在,当列表中的条目被选定后,事件处理器方法会读取authorModel 选定的属性,并且使用选定的条目进行更新。设定该值,然后AuthorModel 会派遣事件,通知程序其它部分已经发生的内容。

 

“为什么不在这里派遣选定事件,然后执行呢?”

 

你也可以这样,如果你的程序很简单的话,这样做也是一个恰当的方法。但是我没有遇到很多这么简单的程序,这就是如何使用Model 的一个范例。因此,我们需要做的工作还很多。鉴于我们已经开始了这个操作,我们还需要将该文本添加到QuoteTextArea, 这样可以进行协调。

 

QuoteTextAreaMediator.as

public class QuoteTextAreaMediator extends Mediator

{

   [Inject]

   public var view:QuoteTextArea;

 

   override public function onRegister(): void

   {

      addContextListener(SelectedAuthorEvent.SELECTED, handleSelectedAuthorChanged)

    }

 

   private function handleSelectedAuthorChanged(event:SelectedAuthorEvent): void

   {

       var author:Author = event.author;

      view. text = author.quote;

    }

}

就是这样了。QuoteTextArea 现在与程序其它部分连接起来。在它的onRegister 里面,我们将使用addContextListener()SelectedAuthorEvent.SELECTED 进行监听,使用with handleSelectedAuthorChanged 进行处理。

 


handleSelectedAuthorChanged 方法从事件中提取信息,更新视图的文本属性(使用选定作者的引言)。此后,我们获得被映射的mediator ,我们将实现本范例的功能性的深度。

 

SimpleListExampleContext.as

override public function startup(): void

{

     injector.mapSingleton(AuthorModel);

    

     mediatorMap.mapView(ListView, ListViewMediator);

     mediatorMap.mapView(QuoteTextArea, QuoteTextAreaMediator);

    

     mediatorMap.mapView(SimpleListExample, ApplicationMediator);

}

有了这个,你就有了范例,该范例包括了在Robotlegs 程序中使用Model 的基本条件。作为数据的护卫,模型在你的程序中发挥了非常重要的作用。利用模型作为数据的读取点,你在数据存储和操作的位置隔离。当你坐下来解决一个问题时,你知道在哪里找关于数据的内容及随后程序状态的显示。如果你的数据散落在程序中,就很难快速隔离问题点,或者给程序添加功能。

 

一篇博文简要介绍了模型。我强烈推荐对MVC 中的M 进行研究。在下一个范例中,我们来看看服务,并且看看在Robotlegs 程序中如何使用它们。在服务之后,我们会查看Commands ,以在讨论下一步课题之前,完成Robotlegs MVC+S 的执行。

 

如果你不能等,在我的博客上(还有互联网的其它网站)有一些文章都是讨论的Robotlegs ,包括这个25 分钟的视频。John Lindquist 在他的博客上有个 Hello World 视频。此外,还有一个最佳操作文件,对很多人都大有裨益。你可以点击Robotlegs 知识库( Robotlegs Knowledge Base )获取帮助和支持。它拥有大批活跃的社区志愿者,包括我自己。志愿者们都很乐于回答有关Robotlegs 的任何问题。此外,我还参与编写了马上要发表的Flex 4 in Action ,这本书有22 页专门讨论 Robotlegs

本文译自:http://insideria.com/2010/06/an-introduction-to-robotlegs-a-1.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值