上一篇介绍了一下Cairngorm的基本组成,下面我给大家展示一个简单的例子。
我们按一个完整的Cairngorm流程来介绍这个例子。这个例子很简单,一个按钮和一个标签,当按钮点下之后标签上的数字开始递增。首先,我们需要一个页面,也就是Cairngorm中的View部分:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
xmlns:model="model.*" xmlns:control="control.*" xmlns:business="business.*"
creationComplete="application1_creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import com.adobe.cairngorm.control.CairngormEventDispatcher;
import control.CountEvent;
import control.MyFrontControl;
import flash.utils.setTimeout;
import model.MyModelLocator;
import mx.events.FlexEvent;
import vo.Num;
private function loggingHandler(evt:MouseEvent):void
{
var num:Num = new Num();
num.startNum = MyModelLocator.getInstance().count;
var countEvent:CountEvent = new CountEvent(num);
CairngormEventDispatcher.getInstance().dispatchEvent(countEvent);
}
protected function application1_creationCompleteHandler(event:FlexEvent):void
{
lable.text = "0";
}
]]>
</fx:Script>
<fx:Declarations>
<control:MyFrontControl/>
<business:MyServiceLocator/>
</fx:Declarations>
<s:Label id="lable" x="430" y="158" width="66" height="22" text="{MyModelLocator.getInstance().count}"/>
<s:Button id="button" x="430" y="201" label="start" width="66" click="loggingHandler(event)"/>
</s:Application>
当用户点击按钮之后,就会调用loggingHandler函数。这loggingHandler中声明了一个自定义Event——CountEvent。
package control
{
import com.adobe.cairngorm.control.CairngormEvent;
import vo.Num;
public class CountEvent extends CairngormEvent
{
public var num:Num = new Num();
public function CountEvent(num:Num)
{
super(MyFrontControl.COUNT_EVENT);
this.num = num;
}
}
}
CountEvent的构造函数中有一句super(MyFrontControl.COUNT_EVENT),在上一篇中我们讲到FrontControl的作用是监听所有Event,其中COUNT_EVENT就是我们所定义的Event的type,记住,CairngormEvent也是继承自Event的。当然,COUNT_EVENT不是凭空产生的,是我们在自定义的FrontControl中定义的。自定义的FrontControl代码如下:
package control
{
import com.adobe.cairngorm.control.FrontController;
import command.CountCommand;
public class MyFrontControl extends FrontController
{
public static var COUNT_EVENT:String = "counting";
public function MyFrontControl()
{
this.addCommand(COUNT_EVENT,CountCommand);
}
}
}
FrontControl管理所有的Event,监听它们并且接受处理它们,回想一下Observer Pattern,我们会在把所有的Observer对象存入Subject中,当事件发生之后Subject会在其内部的表中查找相应的Observer然后调用它们的notify方法。你可以用相同的方法去理解FrontControl的机制,试想,如果我有两个Event,那么FrontControl就应该这样写了:
package control
{
import com.adobe.cairngorm.control.FrontController;
import command.CountCommand;
import command.CountCommand2;
public class MyFrontControl extends FrontController
{
public static var COUNT_EVENT:String = "counting";
public static var COUNT_EVENT2:String = "counting2";
public function MyFrontControl()
{
this.addCommand(COUNT_EVENT,CountCommand);
this.addCommand(COUNT_EVENT2,CountCommand2);
}
}
}
FrontControl相当于一个Subject的实例,它把所有需要监听的Event和其对应的Command名称加入到内部的列表,并且对所有的Event进行监听,当接受的Event之后它就会在内部列表中查找这个Event然后调用相应的Command类的execute()方法。
现在我们就可以很好地理解CairngormEventDispatcher.getInstance().dispatchEvent(countEvent)这句话了,Event被发送之后FrontControl处理之,现在我们来看FrontControl调用的Command是什么样的。
package command
{
import business.CountDelegate;
import com.adobe.cairngorm.commands.ICommand;
import com.adobe.cairngorm.control.CairngormEvent;
import control.CountEvent;
import model.MyModelLocator;
import mx.rpc.IResponder;
import mx.rpc.events.ResultEvent;
import vo.Num;
public class CountCommand implements ICommand, IResponder
{
public function CountCommand(){
}
public function execute(event:CairngormEvent):void
{
var delegate:CountDelegate = new CountDelegate(this);
var countEvent:CountEvent = CountEvent(event);
delegate.count(countEvent.num);
}
public function result(data:Object):void
{
var result:Num = data as Num;
MyModelLocator.getInstance().num = result;
MyModelLocator.getInstance().count = result.startNum;
}
public function fault(info:Object):void
{
}
}
}
我们看到了execute()方法,其实这个方法在接口ICommand中定义。在execute()方法中,Delegate被声明,并且相应的方法被调用。result()方法同样需引起注意,因为它决定了Delegate调用远程服务之后如何处理结果。记住,result()方法是在Delegate中调用的。fault()方法和result()一样,它们都在接口IResponder中定义。
我们接着说自定义的Delegate
package business
{
import com.adobe.cairngorm.business.ServiceLocator;
import flash.utils.clearTimeout;
import flash.utils.setTimeout;
import mx.rpc.IResponder;
import mx.rpc.events.ResultEvent;
import mx.rpc.remoting.RemoteObject;
import vo.Num;
public class CountDelegate
{
private var responder:IResponder;
private var counter:int;
private var remoteObject:RemoteObject;
public function CountDelegate(responder:IResponder)
{
this.responder = responder;
}
public function count(num:Num):void
{
counter = num.startNum;
remoteObject = ServiceLocator.getInstance().getRemoteObject("counting");
remoteObject.addEventListener(ResultEvent.RESULT,OnResult);
counting();
}
private function counting():void
{
remoteObject.getOperation("count").send(counter);
flash.utils.setTimeout(counting, 1000);
}
private function OnResult(evt:ResultEvent):void
{
var result:Num = new Num();
counter = evt.result as int;
result.startNum = counter;
responder.result(result);
}
}
}
这里我们以一个RemoteObject远程调用为例,首先,我们调用的远程服务在ServiceLocator中定义,可以通过remoteObject = ServiceLocator.getInstance().getRemoteObject("counting")来制定这个远程对象,"counting"是在ServiceLocator中定义的RemoteObject id(当然这个ServiceLocator也是我们自定义的,Cairngorm提供的ServiceLocator是一个单例模式的对象)。然后我们为这个远程对象添加ResultEvent的监听器。当我们收到远程调用的结果时,我们调用responder.result()执行在Command中实现的结果处理方法。
下面是我们自定义的ServiceLocator:
<?xml version="1.0" encoding="utf-8"?>
<cairngorm:ServiceLocator xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:cairngorm="com.adobe.cairngorm.business.*">
<fx:Declarations>
<s:RemoteObject id="counting" destination="countingService"/>
</fx:Declarations>
</cairngorm:ServiceLocator>
RemoteObject和Blazeds的配置就不解释了,再附上我们自定义的VO和JAVA服务端代码就完整了。
package vo
{
import com.adobe.cairngorm.vo.IValueObject;
public class Num implements IValueObject
{
public var startNum:int;
}
}
JAVA服务端代码
package cairngorm;
public class CountingService {
public CountingService(){}
public int count(int num){
return ++num;
}
}