(2012-04-03 老物搬运)初识Robotlegs

什么是 robotlegs ?引用百度知道的话:“ Robotlegs 是一个用来开发Flash, Flex, AIR 应用的纯 AS3 微架构(框架). Robotlegs 专注于将应用程序各层排布在一起并提供它们相互通讯的机制. Robotlegs 试图通过提供一种解决常见开发问题的经过时间检验的架构解决方案来加速开发. Robotlegs 无意锁定你到框架, 你的类就是你的类的样子, 而且应该很容易地切换到其他框架.

它与 pureMVC 一样,都是使用 MVC 思想搭建结构的一套框架,他们的最终目的都是使程序松耦合。而它又与 pureMVC 不同

首先他没有像pureMVC那样四处开花,使用event来传递信息的RobotLegs决定了它只能存在在AS平台上;

其次它使用了一个叫做依赖注入的东西,听起来好像比较深奥,事实上就是多使用了一个[Inject]元标签,我们在程序中总是通过这个标签定义的变量来获取某个View component、某个Model等等的引用。而在pureMVC中,我们如果要获得某个引用,一般是使用façaderetrieveMediator(mediatorName)retrieveProxy(proxyName)这样的方法。

最后,使用注入机制的RobotLegs不需要你重新去改写你的类,不像pureMVC那样要求你所有的类继承它提供给你的类。

这些只是我本人看了一两天所感受到的,如果有理解不周全的地方还请指正。。。

 

 

 

下面是我尝试使用 RobotLegs 做的一个简单的留言本 demo 。我会在后面介绍整个 demo 的运作流程(黄字部分


首先简单介绍一下RobotLegs的组成成员,见下图






Context:这是RobotLegs的总线,差不多相当于pureMVCfaçade的概念吧,整个框架的初始化就是从它开始。

View :这部分是你原先就写好的类,他们可能有着自己的事件侦听和简单逻辑——比如 demo 中的隐藏 / 显示历史记录按钮。你需要在 context startup 函数中把你的 view Mediator 相关联,让它作为视图组件的经纪人,管理与其他 actor 的交流。

因此Mediator既可以发送事件到RobotLegs的事件总线(比如:有人按了获取数据按钮了,谁负责加载数据的赶紧的),也可以从总线中侦听事件(比如:管数据的说他的数据已经就绪了,那我直接拿来显示了)。

其实在严格的 MVC 模式下,是不能出现第二种情况的,正确的方式是:管数据的说他数据已经就绪了, RobotLegs 将自动执行一条 Command ,由这 Command 负责把数据交给 Mediator 。也就是说 View Model 之间的沟通必须经过 Controller 但由于 RobotLegs 中,我们无法在 Command 内部轻易获得 view 的引用,所以在 Command 里无法直接对 view 进行操作,折衷的办法可以让 Com mand 再广播一条事件,让 Mediator 侦听来自 command 的事件并对 view 进行操作。

ModelService:它们是处在程序底层的,前者用于长久地保存数据,后者用于与外界沟通解析数据提供给Model。当然你可以使用传统MVC模式,假如舍弃Service,并且没有remoteproxy,你的Model不得不得自己去与外界沟通。ModelService都应当只能广播事件,而不应该去侦听事件,这样可以保持数据的独立性,即使另一个应用程序,只要他使用同样的数据,那么我的类就可以直接拿过去使用。

Controller :前面已经提到, RobotLegs 中的 Controller 就是一条条 Command ,只需要我们事先在 context

而显示历史记录的流程其实大同小异,首先由service类从数据库获取数据,将php页传送来的JSON解析成array+object,然后广播一条解析完毕的事件,该事件对应的Command会自动将解析完毕的结果添加到Model中,Model在完成数据更新后发送一条更新完毕的事件,由输出框来显示它的内容。


中定义好每一条命令与对应的事件,那么当事

件发生时这条命令就自动执行,执行完成后自动消失。

 

首先我们来看一下我们的context类:

package yuyuef

{

    //省略import语句和构造函数等等

   

    publicclass AppContext extends Context

    {

        overridepublicfunction startup():void

        {

            //映射mediator

            this.mediatorMap.mapView(InputArea,InputAreaMediator);

            this.mediatorMap.mapView(OutputArea,OutputAreaMediator);

            this.mediatorMap.mapView(HistroyArea,HistroyAreaMediator);

            //映射command

            this.commandMap.mapEvent(ChatEvent.SUBMIT_MESSAGE,SubmitCommand,ChatEvent,false);

            this.commandMap.mapEvent(ChatEvent.DB_DATA_NEEDED,LoadMessageCommand,ChatEvent,false);

            this.commandMap.mapEvent(ChatEvent.DB_DATA_RECEIVED,ParseMessageCommand,ChatEvent,false);

            //映射model单例

            this.injector.mapSingleton(CurrentMessageModel);

            this.injector.mapSingleton(HistroyMessageModel);

            //映射service单例

            this.injector.mapSingleton(AppServies);

        }

    }

}

我们覆盖了startup方法,这个方法会在程序启动时自动执行,所以在它内部进行一些对应关系的映射。我这里一共分成MVCS4个步骤,将我自定义的MXML组件与mediator相关联、将CommandEvent相关联、注入model、注入service


然后在Main.mxml中引入这个context

<fx:Declarations>

   <!-- 将非可视元素(例如服务、值对象)放在此处 -->

        <yuyuef:AppContext contextView="{this}" />

    </fx:Declarations>


接下来是Mediator(中介器)类,我这个demo分成了3个部分:输入组件(包括输入框、颜色拾取器、提交按钮)、输入组件(即一个TextArea)、历史记录组件(包括按钮和一个TextArea)。我们举输入组件的中介器为例来看看它的源码:



package yuyuef.view.mediator

{

    //各种省略....

   

    publicclass InputAreaMediator extends Mediator

    {

        [Inject]

        publicvar inputArea:InputArea;

       

        overridepublicfunction onRegister():void

        {          

            this.addContextListener(ChatEvent.UPDATE_MESSAGE,onMessageUpdated);

            //错误的写法,直接注册侦听器到子组件,越权

            //this.eventMap.mapListener(inputArea.submitButton,MouseEvent.CLICK,submitMessage);

            this.eventMap.mapListener(inputArea,UserInputEvent.INPUT_DONE,submitMessage);//正确,注册经过父组件过滤后发出的事件

        }

       

        privatefunctiononMessageUpdated(e:ChatEvent):void

        {

            //错误写法,访问了inputArea的子组件,越权

            //inputArea.input.text = '';

            //inputArea.input.setFocus();

            inputArea.clearInput();//正确,通过调用顶级组件的API来改变子组件

        }

       

        privatefunctionsubmitMessage(e:UserInputEvent):void

        {

            var evt:ChatEvent  = newChatEvent(ChatEvent.SUBMIT_MESSAGE,inputArea.message);

            trace('dispatch:'+evt);

            this.dispatch(evt);

        }   }

}



我们在onRegist函数中注册了这个组件可能关心的事件的侦听:点击提交事件和更新信息事件。

可以看到,从 RobotLegs 事件总线中侦听事件我采用了 addContextListener 方法,而侦听鼠标点击按钮事件我采用了 this .eventMap.mapListener eventMap 其实就是一个映射器,他可以将一个事件侦听添加到一个对象上,由于我这个 Mediator [颜色选择器+输入框+发送按钮] 3 者之和而不单单是 [发送按钮] 的中介器,这是一个多个子组件复合而成的复杂组件,我们想侦听其中的某个组件的事件,可以用下面几个办法添加侦听:



this.eventMap.mapListener(inputArea.submitButton,MouseEvent.CLICK,submitMessage);

inputArea.submitButton.addEventListener(MouseEvent.CLICK,submitMessage);

等等,但是这样就破坏了复合组件的完整性。在 Mediator 中,我们不应该访问对应 View 的子组件。因此需要在 View 中对它的子组件进行侦听,然后再发出一个自定义事件,再在 mediator 中对自定义事件进行侦听。


当我们在inputText中输入一些字,并点击发送时,InputAreaMediator首先侦听到CLICK事件,立马广播了一个ChatEvent.SUBMIT_MESSAGE,并且让这条事件携带了用户在它里面输入的message,告诉程序用户需要提交一条这样的message


this.dispatch(evt)方法是robotlegs框架成员(Actor)都具有的一个方法,专门用来向事件总线广播事件



接下来看看SubmitCommand的源码:

package yuyuef.control

{

    //省略省略省略....


publicclass SubmitCommand extends Command

    {

        [Inject]

        publicvar evt:ChatEvent;

        [Inject]

        publicvar currentMessage:CurrentMessageModel;

        [Inject]

        publicvar servies:AppServies;

   

        overridepublicfunction execute():void

        {

            servies.saveMessage(evt.infoasChatVO);

            currentMessage.appendText(evt.infoasChatVO);

        }

     }

}

刚刚输入框的中介器向事件总线中广播了一条ChatEvent.SUBMIT_MESSAGE事件,由于该事件在context里与SubmitCommand关联过,robotLegs在发现该事件后立刻实例化了一个SubmitCommand并执行它的execute函数,即让service去向服务器saveMessage(),同时让currentMessage这个Model更新它的内容。


可以看到,这条command里的eventservicecurrentMessage3个对象都是通过[Inject]注入的,它可以保证我获取到需要的类的实例(这里除了event都是单例的),并进行操作。这个行为有点类似pureMVCfaçade.retrieveProxy(name)方法来获取某个对象。


接下来,servicecommand的控制下访问php,由php连接数据库,尝试保存这条信息,假如成功,php页会返回result=saved,告诉AS保存成功。

下面的我用到的服务器端php代码:

<?php

error_reporting(0);

include_once('dbconnect_function.php');

 

$action= $_POST['action'];

if('save'== $action){

    $body = $_POST['message'];

    $time = $_POST['time'];

    $color = $_POST['color'];

    $sql = "INSERT INTO message (body,time,color) VALUES('$body','$time','$color')";

    $result = yuyuef_mysql_query($sql);

    if($result){

        echo 'result=saved';

    }else{

        echo 'result=unsaved';

    }

    exit();

}

if('load'== $action){

    $sql = 'SELECT * FROM message';

    $result = yuyuef_mysql_query($sql);

    $result = db_result_to_array($result);

    echo json_encode($result);

    exit();

}

 

?>

这段代码先判断 FLASH 的行为,是要保存还是读取消息并执行对应的操作。


service向服务器保存数据的同时,currentMessageModel同时会更新自己的数据,将这段新的消息添加到自身,然后广播一个事件:“我的数据已经被更新了,谁关心的就拿去用”,这个事件又被输入框中介器(InputAreaMediator)和输出框中介器(OutputAreaMediator)所听到,输入框知道自己原先的消息已经进入程序了,就将自己的InputText清空;而输出框则更加关心这个事件所携带的消息,即ChatEvent.info as TextFlow,它会将这个文本流赋予给自己的textFlow属性(spark组件中的textAreatextInput均支持新的TLF框架)用于刷新自己的显示。


到此就是“用户输入留言——该留言保存入数据库——这条留言显示在输出框的”流程


而显示历史记录的流程其实大同小异,首先由service类从数据库获取数据,将php页传送来的JSON解析成array+object,然后广播一条解析完毕的事件,该事件对应的Command会自动将解析完毕的结果添加到Model中,Model在完成数据更新后发送一条更新完毕的事件,由输出框来显示它的内容。

你可能感兴趣的:((2012-04-03 老物搬运)初识Robotlegs)