您的位置:首页 > 其它

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

2010-09-25 10:27 441 查看
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
,并且简要介绍了Robotlegs
的Context
(上下文)与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
举出例子;而Robotlegs
和Flex
一样,使用起来很轻松。所以,我们来看看程序的具体内容吧。

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
拓展 Actor
。Actor
是一个配有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
中。下面是ListView
的mediator


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

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

...
好...

...
真棒!...

首先,我们需要映射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
将更新AuthorModel
。AuthorModel
然后发送事件,通知任何可能任何可能正在监听的人,作者被选定。我们还需要为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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐