云自无心水自闲

天平山上白云泉,云自无心水自闲。何必奔冲山下去,更添波浪向人间!
posts - 288, comments - 524, trackbacks - 0, articles - 6
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理


一、在JavaScript中调用Flex方法
在Flex中可以用ExternalInterface来调用Flex的方法,途径是通过在Flex应用可调用方法列表中添加指定的公用方法。在Flex应用中通过调用addCallback()可以把一个方法添加到此列表中。addCallback将一个ActionScript的方法注册为一个JavaScript和VBScript可以调用的方法。
addCallback()函数的定义如下:
addCallback(function_name:String, closure:Function):void
function_name参数就是在Html页面中脚本调用的方法名。closure参数是要调用的本地方法,这个参数可以是一个方法也可以是对象实例。

举个例子:
<mx:Script>
    import flash.external.*;
    public function myFunc():Number {
        return 42;
    }
    public function initApp():void {
        ExternalInterface.addCallback("myFlexFunction",myFunc);
    }
</mx:Script>
那么在Html页面中,先获得SWF对象的引用,也就是用<object .../>声明的Swf的Id属性,比如说是MyFlexApp。然后就可以用以下方式调用Flex中的方法。
<SCRIPT language='JavaScript' charset='utf-8'>
    function callApp() {
        var x = MyFlexApp.myFlexFunction();
        alert(x);
    }
</SCRIPT>
<button onclick="callApp()">Call App</button>



二、在Flex中调用 JavaScript
你可以调用Html页面中的JavaScript,通过与JavaScript的交互,可以改变Style,调用远程方法。还可以将数据传递给Html页面,处理后再返回给Flex,完成这样的功能主要有两种方法:ExternalInterface()和navigateToUrl()。
在Flex中调用JavaScript最简单的方法是使用ExternalInterface(),可以使用此API调用任意JavaScript,传递参数,获得返回值,如果调用失败,Flex抛出一个异常。
ExternalInterface封装了对浏览器支持的检查,可以用available属性来查看。
ExternalInterface的使用非常简单,语法如下:
flash.external.ExternalInterface.call(function_name: String[, arg1, ...]):Object;
参数function_name是要调用的JavaScript的函数名,后面的参数是JavaScript需要的参数。
举个例子说明如何调用JavaScript函数
Flex应用中,添加如下方法:
<mx:Script>
<?xml version="1.0" encoding="iso-8859-1"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Script>
        import flash.external.*;
    
        public function callWrapper():void {
            var f:String = "changeDocumentTitle";
            var m:String = ExternalInterface.call(f,"New Title");
            trace(m);
        }
    </mx:Script>
    <mx:Button label="Change Document Title" click="callWrapper()"/>
</mx:Application>
Html页面中有如下函数定义:
<SCRIPT LANGUAGE="JavaScript">
    function changeDocumentTitle(a) {
        window.document.title=a;
        return "successful";
    }
</SCRIPT>

posted @ 2006-06-26 14:57 云自无心水自闲 阅读(16566) | 评论 (7)编辑 收藏

一。安装amfphp,
1. 下载ampfphp1.2版本
2. 建立一个目录amfphp, 将包中的文件解压到此目录中。
3. 目录结构举例如下:
c:\amfphp\amf-core
c:\amfphp\browser
c:\amfphp\services
c:\amfphp\gateway.php
在Http Server中(可以是IIS,Apache Http Server)中建立一个虚拟目录,映射c:\amfphp
4. 验证
在浏览器中输入 http://localhost/amfphp/gateway.php
会看到一个成功页面。

二。编写PHP端代码
举个例子:定义一个sample类,这个类编写在sample.php中
其中定义一个getUsers方法
这个php文件放在amfphp\services\中。
<?php
// Create new service for PHP Remoting as Class
class sample
{
        function sample ()
        {
                // Define the methodTable for this class in the constructor
                $this->methodTable = array(
                                "getUsers" => array(
                                "description" => "Return a list of users",
                                "access" => "remote"
                        )
                );
        }

        function getUsers ($pUserName) {
                $mysql = mysql_connect(localhost, "username", "password");
                mysql_select_db( "sample" );
                //return a list of all the users
                $Query = "SELECT * from users";
                $Result = mysql_query( $Query );
                while ($row = mysql_fetch_object($Result)) {
                        $ArrayOfUsers[] = $row;
                }
                return( $ArrayOfUsers );
        }
}
?>
验证:
在浏览器中输入http://localhost/amfphp/browser/index.html
会发现在左边Frame中有一个sampe的链接,点击后,在右边Frame中可以测试此方法。

三。编写Flex端代码
首先写一个RemotingConnection类,继承自NetConnection,主要是用于统一指定编码格式
package
{
        import flash.net.NetConnection;
        import flash.net.ObjectEncoding;
        
        public class RemotingConnection extends NetConnection
        {
                public function RemotingConnection( sURL:String )
                {
                        objectEncoding = ObjectEncoding.AMF0;
                        if (sURL) connect( sURL );
                }
                
                public function AppendToGatewayUrl( s : String ) : void
                {
                }
        }
}
然后在应用中可以进行如下调用:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" creationComplete="initApplication()">
        <mx:DataGrid dataProvider="{dataProvider}">
                <mx:columns>
                                <mx:DataGridColumn headerText="Userid" dataField="userid"/>
                                <mx:DataGridColumn headerText="User Name" dataField="username"/>
                                <mx:DataGridColumn headerText="User Name" dataField="emailaddress"/>
                        </mx:columns>
        </mx:DataGrid>
        <mx:Script>
                <![CDATA[
                        import mx.controls.Alert;
                        [Bindable]
                        public var dataProvider:Array;
                        
                        import flash.net.Responder;
 
                        public var gateway : RemotingConnection;
                        
                        public function initApplication() : void
                        {
                                gateway = new RemotingConnection( "http://localhost/amfphp/gateway.php" );
                                gateway.call( "sample.getUsers", new Responder(onResult, onFault));
                        }
                
                        public function onResult( result : Array ) : void
                        {
                                dataProvider = result;
                                // mx.controls.Alert.show("result: " + result.toString());
                        }
                        
                        
                        public function onFault( fault : String ) : void
                        {
                                // trace( fault );
                                mx.controls.Alert.show(fault);
                        }
                ]]>
        </mx:Script>
</mx:Application>

补充说明:如果调用的PHP函数需要参数,比如:getUsers($user_name)
那么可以在Flex调用端,需要相应的添加此参数:
gateway.call( "sample.getUsers", new Responder(onResult, onFault), "<username>");

posted @ 2006-06-01 17:33 云自无心水自闲 阅读(1598) | 评论 (4)编辑 收藏


MacroMedia的Flex2.0 Beta3发布了。
今天想把项目从Beta2迁移到Beta3下,有一个事情需要注意一下:

<mx:Application />不再支持原有的xmlns="*"

也就是说原来的在同一目录下的使用缺省*作为命名空间的Component在Beta3中会出错,错误提示:
Cann't resolve ... as a component implementation

举个例子说明一下,比如你有一个MXML Application文件名是:main.mxml, 引用了一个名为UserComponent的组件。
<mx:Application xmlns="*">
    <UserComponent id="userComp"/>
</mx:Application>

UserComponent.mxml文件与main.mxml放在一起。这样的做法在Beta2中是OK的。
在Beta3中需要修改:
<mx:Application xmlns:MyComp="*">
    <MyComp:UserComponent id="userComp"/>
</mx:Application>
也就是说必须有一个缺省的NameSpace。

另外Tree中change事件 event.target.selectedNode 属性修改为 event.target.selectedItem

不断补充中。。。。

<mx:tree/>中,folderOpenIcon="UIComponent" 需要修改为: folderOpenIcon="mx.core.UIComponent"
folderClosedIcon也一样。(2006.5.12)


posted @ 2006-05-10 15:51 云自无心水自闲 阅读(882) | 评论 (0)编辑 收藏

最近在搞一个把DocBook的Xml文档转换成Html的工作。
遇到了一些问题,把解决的心得总结一下写在这里。

首先介绍一下DocBook的由来,DocBook是一个开源项目,其实就是一些DTD,规定了书写文档的一些标准。
在此标准下,就可以抛弃Word,WPS之类的工具用写字板来写文档了。
文档此Xml形式保存,需要的时候就使用Xslt通过已有的大量的各种各样的Xsl转换成所需要的格式,比如:RTF,PDF,HTML等等。
这种方式带来了比较大的自由和灵活性。很多人对用这种方式写文档非常推崇。
DocBook网站:http://www.docbook.org, http://docbook.sf.net
讲解DocBook Xsl的一个很好网站:http://www.sagehill.net/docbookxsl
O'reilly出版一本DocBook的书(免费) http://www.oreilly.com/catalog/docbook/chapter/book/docbook.html
DocBook百科全书: http://wiki.docbook.org/topic


遇到的一个主要问题是原来的DocBook就是想把一个Docbook文档中的章节拆分转换生成若干个Html.
查阅文档后得知使用html\chunk.xsl可以实现此功能。
OK,下载Saxon,用Exe4Java生成Windwos平台下的EXE文件后,进行转换。

Good,果然生成了一堆Html,不过为什么Html的文件都是些ch0X.html这样的格式?
再查阅文档,原来这是DocBook的生成规则,第一章生成的Html就取名为ch01.html,
Sect1生成s01,加上所在章节的前缀,如果在第2章,则生成文件名为ch02s01.html,以此类推。
如果想要生成自定义的文件名,就需要在DocBook中指定相应的id属性。如:
<chapter id="workflow">...</chapter>, 那么这一章就会生成workflow.html.

ok, 给DocBook文档中所有章节加上了id属性,指定了文件名。再生成一下。

Faint,失败了,生成的文件名还是老样子?为什么?
再仔细查阅文档,原来在转换的时候少指定了一个参数 use.id.as.filename,
只有此参数值不为0时,上述特性才生效。而缺省值是0.

OK,添加此参数, 执行如下命令:

saxon docbook.xml docbook-xsl-1.69.1\html\chunk.xsl use.id.as.file=1

如果使用xsltproc, 命令格式如下:
xsltproc --stringparam use.id.as.filename 1 ..\docbook-xsl-1.69.1\html\chunk.xsl docbook.xml

Good, 成功了。


最后,推荐一本关于Xslt的好书: Manning - Xslt Quickly, 使用了大量的实例来引导读者一步步,由浅入深逐步学会Xslt

posted @ 2006-04-26 09:42 云自无心水自闲 阅读(755) | 评论 (0)编辑 收藏

在Flex2.0中, Validator组件的使用方式和1.5中相比, 进行了一些改变, 不再需要定义Model, 可以在Validator属性中直接引用Form成员了.
    <mx:Form id="loginForm">
        
<mx:Text text=" {AtsModelLocator.getInstance().loginFailMessage }" width="80%" color="red"/>
    
        
<mx:FormItem label="Username: " required="true">
            
<mx:TextInput id="username" />
        
</mx:FormItem>

        
<mx:FormItem label="Password: " required="true">
            
<mx:TextInput id="password" />
        
</mx:FormItem>
    
</mx:Form>


    
<mx:ControlBar>
        
<mx:Button id="loginSubmit" label="Login" mouseUp="loginUser()"/>
    
</mx:ControlBar>
    
    
<mx:StringValidator id="userNameValidator" source="{username}" property="text"
        tooShortError
="This string is shorter than the minimum allowed length of 3. " 
        tooLongError
="This string is longer than the maximum allowed length of 20." 
        minLength
="4" maxLength="20"/>

    
<mx:StringValidator id="userPassValidator" source="{password}" property="text"
        tooShortError
="This string is shorter than the minimum allowed length of 6. " 
        tooLongError
="This string is longer than the maximum allowed length of 10." 
        minLength
="4" maxLength="20"/>

这样就定义好了两个Validator, 可以对用户名和用户密码进行校验.
但是怎么使用这两个Validator呢?

我是这样用的:
    <mx:Script>
    
<![CDATA[
        import mx.controls.Alert;
        import mx.events.ValidationResultEvent;
        import mx.validators.ValidationResult;  
            
       import com.ats.vo.LoginVO;
       import com.ats.control.LoginEvent;
       
       import mx.validators;
       
       public 
function loginUser() : void
       {
          
if ( ! modelCheckValid ) {
              modelCheckValid 
= true;
              
return;
          }
           
          
var loginVO : LoginVO = new LoginVO();
          loginVO.username 
= username.text;
          loginVO.password 
= password.text;
            
            
var event : LoginEvent = new LoginEvent( loginVO );
            dispatchEvent( event );
       }
       
       private 
var modelCheckValid : Boolean = true;
    ]]
>
    
</mx:Script>

    
<mx:Form id="loginForm">
        
<mx:Text text=" {AtsModelLocator.getInstance().loginFailMessage }" width="80%" color="red"/>
    
        
<mx:FormItem label="Username: " required="true">
            
<mx:TextInput id="username" />
        
</mx:FormItem>

        
<mx:FormItem label="Password: " required="true">
            
<mx:TextInput id="password" />
        
</mx:FormItem>
    
</mx:Form>


    
<mx:ControlBar>
        
<mx:Button id="loginSubmit" label="Login" mouseUp="loginUser()"/>
    
</mx:ControlBar>
    
    
<mx:StringValidator id="userNameValidator" source="{username}" property="text"
        tooShortError
="This string is shorter than the minimum allowed length of 3. " 
        tooLongError
="This string is longer than the maximum allowed length of 20." 
        minLength
="4" maxLength="20"
        invalid
="modelCheckValid=false"
        trigger
="{loginSubmit}"
        triggerEvent
="mouseDown"/>

    
<mx:StringValidator id="userPassValidator" source="{password}" property="text"
        tooShortError
="This string is shorter than the minimum allowed length of 6. " 
        tooLongError
="This string is longer than the maximum allowed length of 10." 
        minLength
="4" maxLength="20"
        invalid
="modelCheckValid=false"
        trigger
="{loginSubmit}"
        triggerEvent
="mouseDown"/>



为什么这么复杂地在Validator中定义trigger, triggerEvent呢?
原因是这样的: 如果不是在Validator的invalid事件中去设置modelCheckValid这个标志量.
就需要在loginUser()函数中对所有Validator进行判断, 代码会显得比较臃肿复杂.
而且如果需要考虑是否需要一次性显示出所有校验失败的错误.
代码示例:
    <mx:Script>
    
<![CDATA[
        import mx.controls.Alert;
        import mx.events.ValidationResultEvent;
        import mx.validators.ValidationResult;  
            
       import com.ats.vo.LoginVO;
       import com.ats.control.LoginEvent;
       
       import mx.validators;
       
       public 
function loginUser() : void
       {
       
          
var vrEvent : ValidateResultEvent;
          
          
var checkFailed : Boolean = false;
          
          vrEvent 
= userNameValidator.validate();
          
if ( vrEvent.results != null && vrEvent.results.length > 0 ) {
              
// 验证失败
              checkFailed = true;
          }
          
          vrEvent 
= userPassValidator.validate();
          
if ( vrEvent.results != null && vrEvent.results.length > 0 ) {
              
// 验证失败
              checkFailed = true;
          }
          
          
if ( checkFailed ) return;
           
          
var loginVO : LoginVO = new LoginVO();
          loginVO.username 
= username.text;
          loginVO.password 
= password.text;
            
            
var event : LoginEvent = new LoginEvent( loginVO );
            dispatchEvent( event );
       }
       
    ]]
>
    
</mx:Script>

    
<mx:Form id="loginForm">
        
<mx:Text text=" {AtsModelLocator.getInstance().loginFailMessage }" width="80%" color="red"/>
    
        
<mx:FormItem label="Username: " required="true">
            
<mx:TextInput id="username" />
        
</mx:FormItem>

        
<mx:FormItem label="Password: " required="true">
            
<mx:TextInput id="password" />
        
</mx:FormItem>
    
</mx:Form>


    
<mx:ControlBar>
        
<mx:Button id="loginSubmit" label="Login" mouseUp="loginUser()"/>
    
</mx:ControlBar>
    
    
<mx:StringValidator id="userNameValidator" source="{username}" property="text"
        tooShortError
="This string is shorter than the minimum allowed length of 3. " 
        tooLongError
="This string is longer than the maximum allowed length of 20." 
        minLength
="4" maxLength="20"/>

    
<mx:StringValidator id="userPassValidator" source="{password}" property="text"
        tooShortError
="This string is shorter than the minimum allowed length of 6. " 
        tooLongError
="This string is longer than the maximum allowed length of 10." 
        minLength
="4" maxLength="20"/>

这种方法也是可行的.
至于具体使用哪一个, 凭自己的喜好了.

posted @ 2006-04-14 11:14 云自无心水自闲 阅读(1821) | 评论 (1)编辑 收藏

这一段时间在 Cairngorm上搭建了一个小项目, 顺便小结一下开发过程:
1. 首先规划构建View, 将一个应用的界面, 分成适当的Mxml Component
2. view中必然涉及的需要数据的绑定, 将组件需要的数据都集中到ModelLocator中.
3. 设计事件(CairngormEvent), 也就是与用户交互的过程中以及系统运转的过程中会需要派发哪些事件,
需要注意的一点是, Cairngorm中Flex事件也需要转化成CairngormEvent
4. 设计事件的处理函数, 也就是命令. 在FrontControl中对事件和命令进行注册.
5. 命令中通过代理去调用服务.
5. 设计代理类, 在代理类中调用服务
5. 设计命令中涉及的服务(最可能的是与数据库的交互), 并添加相应的配置
6. 设计服务中需要使用的ValueObject
7. 命令中如果需要对视图组件数据进行存取, 需要通过ViewHelper来完成, 设计相应的ViewHelper.
同时在mxml中对viewHelper进行注册.

补充说明: 事件的产生不一定全部是与用户交互的结果, 也就是说不全是由View产生的.
当然大部分的事件(比如用户点击了保存按钮)是这样产生的.
在命令中也可以产生命令, 典型的就是SequenceCommand, 应用中可以把一个事件的处理分成几个步骤来完成,
完成第1个步骤后怎么通知第2个步骤开始呢, 当然还是继续派发事件啦. 在SequenceCommand在类中,
把派发事件封装了一下, 给出了一个executeNextCommand()可以直接调用.
不过在这里我也遇到了一个问题, 直接使用SequenceCommand的executeNextCommand()并不管用.
好象dispatchEvent并没有效果, 我后来是自己修改了代码, 使用Application.application.dispatchEvent才解决问题的.

Cairngorm的优点:
一. 实现了比较彻底的解耦
1. 事件机制, 对用户的响应(比如点击保存按钮), 并不是直接从View中抓取数据, 然后New一个类, 调用这个类的某个方法,
将数据保存到数据库中, 而只是简单地派发一个事件, 具体事件由谁来响应, 如何处理对他来说是透明不可见的.
2. Locator模式, Cairngorm中service, view, model的获取都是通过Locator的,
也就是说系统其他部分对于service, view, model只需要知道其ID就够了, 其内部实现等待细节都是不需要知道的.
举个例子, 传统的方法: 你要找一个叫张三的人帮你干一件事, 你需要知道张三长什么样,
然后在一个坐着10个人的大办公室里找到他, 告诉他你的要求.
而现在在这个大办公室的门口多了一个前台小姐, 你只需要告诉这个小姐,我要找张三, 然后她会帮你去找,
你根本不需要知道张三的模样.

RIA的优点: 由于Flex的原因, 系统处理是异步的.
比如, 你请求了一个比较耗时的数据库读取操作, 请求发出后, 你就可以进行其他操作了, 服务结束会产生相应事件,
然后由Command进行后续处理, 最后引发页面数据更新.

posted @ 2006-04-05 13:51 云自无心水自闲 阅读(2122) | 评论 (0)编辑 收藏

进行了一系列的改动。最明显的是Xml命名空间的变化.
从www.macromedia.com/2005/xml 修改为 www.adobe.com/2006/xml
其次变量、和成员的绑定变得更简单,对于[Bindable]属性的变量或者类成员。
可以不需要手工指定并派发事件,系统会自动进行。
Beta1代码示意:
private var _fieldTest:String;
[Bindable(
"textChanged")]
public 
function get fieldTest() : String{ return _fieldTest;}
public 
function set fieldTest(s : String) {
    _fieldTest 
= s;
    dispatchEvent(
new Event("textChanged"));
}
Beta2代码示意:
private var _fieldTest : String;
[Bindable]
public 
function get fieldTest() { return _fieldTest; }
public 
function set fieldTest(s : String) {
    _fieldTest 
= s;
}
有所简化。

另外一些多余的属性和方法被取消。
一些类、属性、方法改变名字。

posted @ 2006-03-31 23:34 云自无心水自闲 阅读(220) | 评论 (0)编辑 收藏

     摘要: 最近做一个项目的时候,需要将数据库从原先的SqlServer迁移到Oracle中。需要迁移的不仅是数据还需要将表结构、存储过程、视图、触发器.... 所有东西都迁过去。于是在网上搜索了一下,很快找到了www.swissql.com中提供了这样的工具。但是能下载的是30天有效。只能转换2000行Sql文本的试用版。自己动手、丰衣足食。开始破解:1. 安装SwisSql2. 把SwisSql的Lib目...  阅读全文

posted @ 2006-03-31 23:26 云自无心水自闲 阅读(5658) | 评论 (23)编辑 收藏

感觉目前采用插件模式进行功能扩展的东西越来越多了.
从Eclipse开始, 确实给软件带来巨大的便利.
可扩展性得到了淋漓尽致的体现.

言归正传, 列举一下我目前使用的FireFox的扩展:
Tab Mix Plus: 扩展FireFox的标签功能
All-in-One Gestures: 让你使用鼠标手势来执行一般的命令。包括摇杆导航.滚轮导航和页面卷轴等等。
FasterFox: 给FF提速. 感觉好像是快了一些.
FlashGot: 允许 Firefox、Mozilla Suite、Netscape 和 Thunderbird 使用大多数流行的外部下载管理器处理单一的和全部("全部" 和 "选择")下载, 现在可以抛弃FF那个看上去土土的下载器了. 如果你安装的是迅雷的话, 他自带一个FireFox的插件, 就不需要装这个了.
Colorful Tab: 让每个标签使用不同的颜色使其易于识别,可以将非活动的标签加淡化效果, 并可以设置淡化的度. 而漂亮的界面总体上也更具魅力。好象没有在其他地方看到过. 现在页面感觉更加生动活泼.
Image Zoom:增加图像缩放功能。右键点击一个图像,从弹出菜单选择一个缩放选项。或者在按下鼠标右键的同时转动滚轮来缩放图像。
Restart Firefox:重新启动Firefox, 安装新的扩展后, 点这个就行了.

Gmail Manager: Gmail邮件管理器. 比Gmail notifier好。

Reveal:让你可以一次性浏览所有标签页.
ScapBook:网页收藏夹.

Adblock Plus:拦截网页广告
NoScript: 阻止网页中Script代码的运行。
Drag de Go: 既可根据拖动对象的不同也可以根据拖动方向的不同 业定制不同的动作。十分的个性化。

IE Tab: 推荐使用, 可以在FireFox中使用IE内核显示页面. 插件安装后会在StatusBar(可以配置,也可以显示在工具栏)中显示一个小图标, 左键点击小图标即可切换浏览器的内核. 非常爽! 这样对于一些FireFox兼容不好的网站就不需要再打开一个IE浏览器了, 在FireFox中全部搞定.

Load Time Analyzer: 会在一个工具条上显示网页加载的事件和时间, 并能够用图形直观的显示出来.
View Source Chart: 可以把网页的源代码进行格式化, 使用颜色加以区分.

Google Notebook: Google推出的网页便签类产品, 在浏览时看到发好的东西, 可以随时记录下来, 好处是: 一处记录, 到处浏览. 因为他的记录会保存在GMail的服务器中.

FoxyProxy: 好用的代理切换, 如果使用Tor或者代理的话, 可以方便地切换使用哪个代理.
Download Embedded: 用于下载页面内嵌的对象, 比如视频,mp3,Flash等等.

posted @ 2006-03-16 16:37 云自无心水自闲 阅读(326) | 评论 (0)编辑 收藏

先简要地介绍一下Cairngorm中采用的设计模式:
Cairngorm框架最大的革新是将用户行为和系统级事件统一地映射为Cairngorm事件.
当组件接收到用户行为或者系统事件后, 用户请求被转换成组件可以传播的内部事件. RIA中处理用户请求不需要到服务器去转一圈.
当用户行为指定要执行一个功能时, Cairngorm要求广播一个合适的事件.
在设计模式中命令模式特别适合此种情形. 在这个模式中, 将实现功能的类称之为命令(Command).
每一个而且是所有的命令提供一个单点入口, 一个execute()方法.
这样允许第3方调用此命令, 而不需要了解命令具体是如何实现的.
通常这些命令被叫作"Worker", 因为他们承担了在应用背后进行工作的任务.

我们现在开始根据示例来研究Cairngorm Store.
看一下Cairngorm Store关键功能之一: 将商品添加到购物车中.
为实现此功能, 创建一个新的命令类: AddProductToShoppingCartCommand
import org.nevis.cairngorm.commands.Command;
import org.nevis.cairngorm.control.Event;
import org.nevis.cairngorm.samples.store.model.ModelLocator;
import org.nevis.cairngorm.samples.store.vo.ProductVO;

class org.nevis.cairngorm.samples.store.command.AddProductToShoppingCartCommand  implements Command  
{
    public 
function execute( event : Event ):Void
    {
        
var product : ProductVO = ProductVO( event.data.product );
        
var quantity : Number = Number( event.data.quantity );
        ModelLocator.shoppingCart.addElement( product, quantity );
    }    
}
这个类看起来并不复杂. 首先一个具体的类实现了Cairngorm的命令接口.
如果你查看了Cairngorm的源码, 你会发现这个接口只是简单地规定了命令必须实现一个方法: execute() 作为入口.

看一下execute()方法的实现, 可以发现事件是如何执行包含ProductVO值对象和数量的命令的.
VO和数量是预先装载在Event类中的. Event也是一个Caringorm定义的类, 其中包括事件的类型和事件的方法.
购物车属于客户端数据, 因此它存放于ModelLocator类中. 所以,命令只是添加适当数量的商品到购物车中, 使用购物车提供的方法.
这就是创建一个简单功能命令类的所有工作. 命令查询事件, 获取事件相关数据.
如果执行的命令更改应用的数据,比如要求在购物车视图中新增一个商品, 应用需要使用ModelLocater完成更改客户端数据.

有一个非常重要的设计概念在这里强调一下. 在前面的示例中,
所有复杂的业务逻辑(比如一个购物车可以做什么,不可以做什么)都被封装在一个类中(称之为ShoppingCart).
比如: 一个用户添加一个商品到购物车中, 如果购物车没有此种商品, 则新增一个, 如果已经存在, 则将数量加1.

Cairngorm并不减轻开发者创建业务对象的工作. 特别之处只是在于它实现业务域的类.

开发者应该从Cairngorm架构中抽离出来, 把业务逻辑从命令中提取出来放入类中.
一个典型的实现方法是进行抽象类的重构. 此项技术的好处在于可以进行单元测试, 书写API文档, 使用其他应用开发者可以进行复用.
Caringorm商店中的购物车类是遵循此原则的极好的例子.



借鉴设计模式的思想, Cairngorm对客户事件的进行响应而不是对服务器HTTP的进行响应,
Cairngorm使用前台控制(Front Controller)模式作为所有Cairngorm事件的统一入口.
class org.nevis.cairngorm.samples.store.control.ShopController extends FrontController
{
    public 
function ShopController()
   {
       initialiseCommands();
   }
    
    
//----------------------------------------------------------------------------

    public 
function initialiseCommands()
    {
      addCommand( ShopController.EVENT_GET_PRODUCTS, 
new GetProductsCommand() );
      addCommand( ShopController.EVENT_ADD_PRODUCT_TO_SHOPPING_CART, 
new AddProductToShoppingCartCommand() );
      addCommand( ShopController.EVENT_DELETE_PRODUCT_FROM_SHOPPING_CART, 
new DeleteProductFromShoppingCartCommand() );  
      addCommand( ShopController.EVENT_FILTER_PRODUCTS, 
new FilterProductsCommand() );     
      addCommand( ShopController.EVENT_SORT_PRODUCTS, 
new SortProductsCommand() );     
      addCommand( ShopController.EVENT_VALIDATE_ORDER, 
new ValidateOrderCommand() );
      addCommand( ShopController.EVENT_VALIDATE_CREDIT_CARD, 
new ValidateCreditCardCommand() );     
      addCommand( ShopController.EVENT_COMPLETE_PURCHASE, 
new CompletePurchaseCommand() );     
    }
    
    
//-------------------------------------------------------------------------

    public static 
var EVENT_GET_PRODUCTS = "getProducts";
    public static 
var EVENT_ADD_PRODUCT_TO_SHOPPING_CART = "addProductToShoppingCart";
    public static 
var EVENT_DELETE_PRODUCT_FROM_SHOPPING_CART = "deleteProductFromShoppingCart";  
    public static 
var EVENT_FILTER_PRODUCTS = "filterProducts";
    public static 
var EVENT_SORT_PRODUCTS = "sortProducts";
    public static 
var EVENT_VALIDATE_ORDER = "validateOrder";
    public static 
var EVENT_VALIDATE_CREDIT_CARD = "validateCreditCard";
    public static 
var EVENT_COMPLETE_PURCHASE = "completePurchase";            
        
}

构造函数调用initialiseCommands(), 将广播的事件委派给相应的命令去处理.

我们看一个添加商品到购物车的例子. 当应用广播ShopController.EVENT_ADD_PRODUCT_TO_SHOPPING_CART命令时,
前台控制中下面这行代码保证AddProductToShoppingCartCommand的execute()方法被调用.

addCommand( ShopController.EVENT_ADD_PRODUCT_TO_SHOPPING_CART, new AddProductToShoppingCartCommand() );

ShopController继承了Cairngorm中的FrontController基类, 因此可以使用addCommand()来给事件注册相应的命令.
Cairngorm底层架构完成了剩余部分的工作. 应用中任意地方简单地广播适当的事件, Cairngorm确保相应的命令被触发.

另外需要做的是在Mxml的主入口点创建控制器. 在Cairngorm商店中, 是Main.mxml, 代码如下:
<control:ShopController id="controller" />

"Control"的命名空间在应用标签中定义, 指定如下Cairngorm包:
xmlns:control="org.nevis.cairngorm.samples.store.control.*"

你只需要这样做,就可以保证应用拥有一个前台控制模式, 响应所有的事件, 并触发你使用addCommand()注册的命令.




posted @ 2006-03-14 20:26 云自无心水自闲 阅读(3908) | 评论 (0)编辑 收藏

仅列出标题
共29页: First 上一页 21 22 23 24 25 26 27 28 29 下一页