Spring ActionScript Operation API入门教程[一]:简介

在Spring ActionScript 1.0 RC版本中,包含了一个Operation API,今天我们就来对这个部分做个简单介绍。在之后的文章中,我们将练习使用Operation API来完成一个简单的示例。

Operation API简介

在很多Flash或Flex应用中有一个共性:他们都需要连接到后端获取数据,比如:呼叫一个远程对象,加载一个二级模块或资源文件,这些方式也有一个共性:他们都是异步的。而Flex框架和Flash Player看起来为不同类型的数据检索提供了不同的异步模式,比如回调方法,基于事件的方法,呼叫一个实例返回一个IEventDispatcher或其它接口,等等。这些看起来有些混乱,也使我们的编程方式看起来不统一。

在这种情况下,Spring ActionScript Operation API出现了,它提供了一种通用的方式,包含了上面不同的模式并非常易于使用。它提供了一些类和接口来让开发者非常容易编写他们的逻辑,同样对于一些通用的任务,Spring ActionScript也提供了支持,比如加载Flex模块,资源模块和远程对象调用。

注意Operation API不是一个MVC或MVCS框架,作者认为提供一个框架过于僵硬,不如给开发者提供一套基础的接口和类更方便他们的使用。每个应用都有自己的特点,而Operation API的灵活和便捷将使它很容易整合到应用中。

Operations, commands, services and tasks

如标题所述,Operation API包含4个部分:

  1. Operation: 一个异步的动作.

  2. Command: 一个延期执行的动作.

  3. Service:一组相关的Operation的集合.

  4. Task: 一组被工作流控制的Command的集合.

Operation(操作)

操作是一个异步的动作,比如我们向服务器发送一次请求,然后稍等片刻,获取到了服务器返回的数据,可以将这个过程抽象为一个操作。在编程层面,我们把一个操作对应到具体的类,则这个类需要实现操作的接口:

  1. public interface IOperation extends IEventDispatcher {
  2.  
  3. function get result():*;
  4.  
  5. function get error():*;
  6.  
  7. function addCompleteListener(listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void;
  8.  
  9. function addErrorListener(listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void;
  10.  
  11. function removeCompleteListener(listener:Function, useCapture:Boolean = false):void;
  12.  
  13. function removeErrorListener(listener:Function, useCapture:Boolean = false):void;
  14.  
  15. }

注意这个接口规范了异步的操作模式,包括返回的结果或错误,对过程使用事件侦听。如果你需要侦听数据的加载进度,则要实现下面的接口:

  1. public interface IProgressOperation extends IOperation {
  2.  
  3. function get progress():uint;
  4.  
  5. function get total():uint;
  6.  
  7. function addProgressListener(listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void;
  8.  
  9. function removeProgressListener(listener:Function, useCapture:Boolean = false):void;
  10. }

操作可以说是Operation API概念中的最小单元,因为其有规范的接口,我们可以很方便的在Command和Service等部分来使用它。我们甚至可以把若干小的操作集合起来使用,这就要用到OperationQueue类,示例如下:

  1. var queue:OperationQueue = new OperationQueue();
  2. queue.addCompleteListener(handleQueueComplete);
  3. queue.addOperation(new FirstOperation());
  4. queue.addOperation(new SecondOperation());
  5. queue.addOperation(new ThirdOperation());
  6. queue.addOperation(new FourthOperation());

另外Spring ActionScript也封装好了一些通用的操作,列表如下:

Command(命令)

命令是需要延期执行的动作。这个概念跟Cairngorm中的Command很类似,它负责具体的逻辑处理,需要一定的机制来触发。在Cairngorm中是由FrontControler将事件映射到Command来触发Command的执行。而在Operation API中比较灵活,并没有规定由谁来触发Command,而很多情况下我们可以把它和Task结合起来使用,功能强大,后面会做介绍。Command的接口也比较简单:

  1. public interface ICommand {
  2.  
  3. function execute():*;
  4.  
  5. }

继承此接口需要实现execute的方法。跟操作类似,我们也可以把若干命令集合起来使用,这里要使用CompositeCommand类。注意你可以定义执行的方式(并行或顺序执行):

  1. var compositeCommand:CompositeCommand = new CompositeCommand(ComposeiteCommandKind.SEQUENCE);
  2. compositeCommand.addCommand(new FirstCommand());
  3. compositeCommand.addCommand(new SecondCommand());
  4. compositeCommand.addCommand(new ThirdCommand());
  5. compositeCommand.addCommand(new FourthCommand());
  6. compositeCommand.addCompleteListener(handleCompositeCommandComplete);
  7. compositeCommand.addErrorListener(handleCompositeCommandError);
  8. compositeCommand.execute();

我们通常不会直接调用一个操作,而是调用命令(操作一般放到命令或服务里调用),而为每一个操作都对应一个命令的类并不现实,这时我们可以使用GenericOperationCommand。

  1. var genericOperationCommand = new genericOperationCommand(LoadModuleOperation,['module.swf']);
  2. genericOperationCommand.addCompleteHandler(operationCompleteHandler);
  3. genericOperationCommand.execute();

Service(服务)

服务的实现则完全跟我们的业务逻辑相关,我们首先需要定义接口,抽象出那些需要跟后端交互的方法。比如我们的应用中要实现用户信息操作的服务,则接口定义如下:

  1. public interface IUserService {
  2.  
  3. function createUser():IOperation;
  4.  
  5. function updateUser(user:User):IOperation;
  6.  
  7. function deleteUser(user:User):IOperation;
  8.  
  9. }

服务的实现,这里选择继承RemoteObjectService来简化操作:

  1. public class UserService extends RemoteObjectService implements IUserService {
  2.  
  3. public function UserService(remoteObject:RemoteObject) {
  4. Assert.notNull(remoteObject,"remoteObject argument must not be null");
  5. super(remoteObject);
  6. }
  7.  
  8. public function createUser():IOperation {
  9. return call('createUser');
  10. }
  11.  
  12. public function updateUser(user:User):IOperation {
  13. return call('updateUser',user);
  14. }
  15.  
  16. public function deleteUser(user:User):IOperation {
  17. return call('deleteUser',user);
  18. }
  19. }

然后我们可以在命令中调取服务,来做具体的逻辑判断:

  1. public class CreateUserCommand extends AbstractOperation implements IAsyncCommand {
  2.  
  3. private var _userService:IUserService;
  4. private var _applicationModel:IApplicationModel;
  5.  
  6. public function CreateUserCommand(userService:IUserService, applicationModel:IApplicationModel) {
  7. Assert.notNull(userService,"userService argument must not be null");
  8. Assert.notNull(applicationModel,"applicationModel argument must not be null");
  9. _userService = userService;
  10. _applicationModel = applicationModel;
  11. }
  12.  
  13. public function execute():* {
  14. var operation:IOperation = _userService.createUser();
  15. operation.addCompleteListener(handleComplete);
  16. operation.addErrorListener(handleError);
  17. }
  18.  
  19. protected function handleComplete(event:OperationEvent):void {
  20. _applicationModel.users.addItem(event.result as User);
  21. dispatchCompleteEvent(event.result);
  22. }
  23.  
  24. protected function handleError(event:OperationEvent):void {
  25. dispatchErrorEvent(event.error);
  26. }
  27.  
  28. }

Task(任务)

为了可以方便的批量执行Command,Spring ActionScript引入了Task的概念。一个Task是若干Command的集合,但不仅仅如此,Task允许你定义Command的执行顺序,甚至可以引入循环和判断。首先预览一下ITask这个接口:

  1. public interface ITask extends ICommand, IOperation {
  2.  
  3. function get parent():ITask;
  4. function set parent(value:ITask):void;
  5.  
  6. function next(command:ICommand):ITask;
  7.  
  8. function and(command:ICommand):ITask;
  9.  
  10. function if_(condition:IConditionProvider=null, ifElseBlock:IIfElseBlock=null):IIfElseBlock;
  11.  
  12. function else_():IIfElseBlock;
  13.  
  14. function while_(condition:IConditionProvider=null, whileBlock:IWhileBlock=null):IWhileBlock;
  15.  
  16. function for_(count:uint, countProvider:ICountProvider=null, forBlock:IForBlock=null):IForBlock;
  17.  
  18. function exit():ITask;
  19.  
  20. function reset(doHardReset:Boolean = false):ITask;
  21.  
  22. function pause(duration:uint, pauseCommand:ICommand=null):ITask;
  23.  
  24. function end():ITask;
  25. }

Task类实现了这个接口,如前所述,我们可以定义Command的执行顺序,比如我们要并行执行,代码如下:

  1. var task:Task = new Task().and(new FirstCommand()).and(new SecondCommand()).and(new ThirdCommand()).and(new FourthCommand());
  2. task.addEventListener(TaskEvent.TASK_COMPLETE, handleTaskComplete);
  3. task.execute();

我们还可以使用next方法,含义是先执行前面的Command,等待执行完毕之后,再执行下一个Command。比如:

  1. var task:Task = new Task().and(new FirstCommand()).and(new SecondCommand()).next(new ThirdCommand()).next(new FourthCommand());
  2. task.addEventListener(TaskEvent.TASK_COMPLETE, handleTaskComplete);
  3. task.execute();

在Task里执行For循环:

  1. var task:Task = new Task();
  2.  
  3. task.for_(10)
  4. .next(new FirstCommand())
  5. .end();
  6.  
  7. task.addEventListener(TaskEvent.TASK_COMPLETE, handleTaskComplete);
  8. task.execute();

我们也可以使用If判断,条件是一个实现IConditionProvider接口的类:

  1. public interface IConditionProvider {
  2. function getResult():Boolean;
  3. }

  1. var task:Task = new Task();
  2.  
  3. task.if_(new ConditionProvider())
  4. .next(new FirstCommand())
  5. .else_()
  6. .next(new SecondCommand())
  7. .end();
  8.  
  9. task.addEventListener(TaskEvent.TASK_COMPLETE, handleTaskComplete);
  10. task.execute();

还可以使用While循环:

  1. var task:Task = new Task();
  2.  
  3. task.while_(new MyConditionProvider())
  4. .next(new FirstCommand())
  5. .end();
  6.  
  7. task.addEventListener(TaskEvent.TASK_COMPLETE, handleTaskComplete);
  8. task.execute();

下面我们看看如何在XML配置中使用Task,首先是命名空间的配置:

  1. <objects
  2. xmlns="http://www.springactionscript.org/schema/objects"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:t="http://www.springactionscript.org/schema/task"
  5. xsi:schemaLocation="
  6. <a href="http://www.springactionscript.org/schema/objects" title="http://www.springactionscript.org/schema/objects">http://www.springactionscript.org/schema/objects</a> <a href="http://www.springactionscript.org/schema/objects/spring-actionscript-objects-1.0.xsd<br />
  7. " title="http://www.springactionscript.org/schema/objects/spring-actionscript-objects-1.0.xsd<br />
  8. ">http://www.springactionscript.org/schema/objects/spring-actionscript-obj...</a> <a href="http://www.springactionscript.org/schema/task" title="http://www.springactionscript.org/schema/task">http://www.springactionscript.org/schema/task</a> <a href="http://www.springactionscript.org/schema/util/spring-actionscript-task-1.0.xsd"></p>
  9. <p>" title="http://www.springactionscript.org/schema/util/spring-actionscript-task-1.0.xsd"></p>
  10. <p>">http://www.springactionscript.org/schema/util/spring-actionscript-task-1...</a> <!-- further markup ommitted -->
  11. </objects>

要使用Task的命名空间,你需要为上下文添加TaskNamespaceHandler的类:

  1. applicationContext.addNamespaceHandler(new TaskNamespaceHandler());

这样你就可以在XML配置中使用Task了,一个简单的Task配置如下:

  1. <t:task id="testTask" scope="prototype">
  2. <t:and>
  3. <object id="command1" scope="prototype" class="classes.commands.FirstCommand"/>
  4. </t:and>
  5. <t:and>
  6. <object id="command2" scope="prototype" class="classes.commands.SecondCommand"/>
  7. </t:and>
  8. <t:next>
  9. <object id="command3" scope="prototype" class="classes.commands.ThirdCommand"/>
  10. </t:next>
  11. <t:next>
  12. <object id="command3" scope="prototype" class="classes.commands.FourthCommand"/>
  13. </t:next>
  14. </t:task>

你可以把Command单独配置,修改如下:

  1. <object id="command1" scope="prototype" class="classes.commands.FirstCommand"/>
  2. <object id="command2" scope="prototype" class="classes.commands.SecondCommand"/>
  3. <object id="command3" scope="prototype" class="classes.commands.ThirdCommand"/>
  4. <object id="command3" scope="prototype" class="classes.commands.FourthCommand"/>
  5.  
  6. <t:task id="testTask" scope="prototype">
  7. <t:and command="command1"/>
  8. <t:and command="command2"/>
  9. <t:next command="command3"/>
  10. <t:next command="command4"/>
  11. </t:task>

加入判断:

  1. <object id="command1" scope="prototype" class="classes.commands.FirstCommand"/>
  2. <object id="command2" scope="prototype" class="classes.commands.SecondCommand"/>
  3. <object id="myCondition" scope="prototype" class="classes.condition.MyConditionProvider"/>
  4.  
  5. <t:task id="testTask" scope="prototype">
  6. <t:if condition="myCondition">
  7. <t:next command="command1"/>
  8. <t:else/>
  9. <t:next command="command2"/>
  10. </t:if>
  11. </t:task>

参考网页:

http://www.springactionscript.org/docs/reference/html/the_operation_api.html

在下一篇文章中我们将使用Operation API完成一个实例,敬请关注.

riadevID: 
您给予的分值: None 平均分: 7 ( 3 票)

发表新评论

  • 网页地址和电子邮件地址将会被自动转换为链接。
  • 行和段被自动切分。
  • 您可以使用下面的标签来高亮显示您的评论内容: <code>, <blockcode>. 可以使用"[foo]".旁边显示标签样式 "<foo>" PHP代码可以用这样的区块来包含<?php ... ?> or <% ... %>

更多格式化选项信息

验证区域
系统验证:请回答下面的问题