板凳一族必备软件。。。

最近深蹲把腰伤了,少了每天去健身房的锻炼,坐的时间长了脖子特别疼。灰长灰长的严重,最后椅子上靠近腰和脖子的位置都垫了垫子,靠在上面,仿佛变成了霍金同学  霍金同学 额,是他使用电脑发音以前 :roll:

重点是,这种病应该不属于我这个年纪才对啊。。。找了半天(感谢克里斯同学)最后找到了这个酷软

AntiRSI

http://tech.inhelsinki.nl/antirsi/

好用到不行,强烈推荐。 (windows 上可以试试这款 workrave

Cairngorm In One File

最近有机会研究了一下传说中的 Cairngorm 框架,整体感觉还不错,其中的模块化概念可以使项目在变大变复杂之后依然可控。

具体优点就不说了,说说最大的缺点,就是 繁琐 。由于要分离用户触发事件以及对应的 Command 操作,导致几乎每个明显的用户操作都要写一个 Event 类(还真是累阿);另外由于 Cairngorm 本身是一个 design patten 的实践,不是 library,所以造成实际使用的时候需要死版的 implement 一些不着调的空 interface。

简单来说 Cairngorm 是一个婆婆妈妈的 framework,这从技术上来说也不是什么问题,但是重点是会影响写代码人的心情。比如我,一想到要每一个 action 都要写一个没什么特别之处的 Event class 就觉得头疼,一头疼就不想干,一不想干就完了。。。

所以,我就想能不能保留 Cairngorm 的思想,但是用一种更简洁的方式来实现?最后不小心想出了一招,经过一段时间的实验,感觉效果还不错,和大家分享一蛤。

global.as

我把 Cairngorm 的思想融入到一个名叫 global 的类中,使用的时候只需要这一个文件就包含了全部 Cairngorm 相关内容。我打算用一个通过 webservices 查询当前货币兑换比例的例子来具体的说一下 Cairngorm 各方面的特性,以及如何用 global 来实现这些特性。

全局的数据管理

Cairngorm 通过一个 singleton 的 ModelLocator 来连接数据与用户界面,基本思路是全部 ui 相关的数据都不是直接设置到组件上,而是通过一个全局的对象借由 binding 来实现。这样做分离了组件、数据以及命令之间的依靠。当有某个命令修改了某一个数据源之后,不用主动的再去更新组件显示,所有已经设置对应 binding 的组件将会自动更新。

global.as 将自身作为 ModelLocator 来使用,相关代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// bindable properties, access from global.data model locator.
public var fromCurrency:String=“USD”;
public var toCurrency:String=“CNY”;
public var currencyResult:String=“”;
public var state:String=STATE_LOAD;
public var enabled:Boolean=true;
/**
 * The global singleton model locator.
 */

public static function get data():global
{
    return _data;
}

然后在对应需要响应数据更新的组件上通过绑定 global.data 中的对应属性来实现更新。比如实例中用来显示货币兑换比例结果的 Label 组件绑定了 global.data.currencyResult 属性。

1
<mx:Label text="{global.data.currencyResult}" color="0x00ff00"></mx:label>

全局的事件播报以及响应

在 Cairngorm 中,程序通过全局的 EventDispatcher 来播报全部程序相关的事件,然后对应处理事件的 Command 则通过 FrontController 在程序初始阶段配置好与对应事件的监听关系。副作用是:随着程序复杂程度的增加,会出现一堆只是名字不同的 Event class,以及只为了 execute 一下下却不得不实现的 ICommand 类。

global.as 通过静态方法 fire/listen/unlisten 实现了全局的事件播报以及监听功能,其中为了避免过多 Event class 的出现,fire 方法会播报出 DynamicEvent,并允许设置任意参数到 event 对象上,在实例中当用户点击了 Calculate Conversion Rate 按钮后会这样触发事件

1
click="global.fire(global.EVENT_CONVERSION_RATE,{fromCurrency:fromField.text,toCurrency:toField.text})"

另一方面 command 响应事件,也做了相应的简化:直接用 function 作为 command 来进行实际的业务操作:

1
2
3
4
5
6
7
public function conversionRate(e:DynamicEvent):void
{
    //..
    var fromCurrency:String=String(e.fromCurrency);
    var toCurrency:String=String(e.toCurrency);
    //..
}

最后在程序初始化的时候将 command 和对应的事件建立联系(类似 Cairngorm 中 FrontController 的功能)

1
2
3
4
5
6
function registerDefaultCommands():void
{
    //register default commands here.
    global.listen(global.EVENT_LOAD_WSDL,loadWsdl);
    global.listen(global.EVENT_CONVERSION_RATE,conversionRate);
}

全局的网络服务资源

最后关于远程 service 的部分,Cairngorm 提供了另外一个 singleton 的 ServiceLocator 供程序使用,并通过 Responder 来响应服务返回的结果。

在 global.as 中通过一个 rpc 方法实现了注册以及使用 service 的功能。比如实例中 webservices 的注册如下:

1
2
3
4
5
6
7
function registerDefaultServices():void
{
    //register default services here.
    var s:WebService=new WebService();
    //..
    global.rpc(global.CONVERSION_DESTINATION,s);
}

然后代码通过以下方法访问上面注册的 service:

1
2
3
4
public static function get currencyConvertor():*
{
    return rpc(CONVERSION_DESTINATION);
}

实际上 rpc 方法返回的是一个 service 的 proxy 对象。和直接使用 service 不同的是,通过 proxy 简化了服务方法调用后结果响应部分的代码。比如实例中获取货币兑换比率的方法是这样写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
global.currencyConvertor.ConversionRate(fromCurrency,toCurrency)(
    function(data:Object):void
    {
        global.data.currencyResult=data.result.toString();
    },
    function(info:Object):void
    {
        Alert.show(info.fault.message,info.fault.faultString);
    },
    function(o:*):void
    {
        global.data.enabled=true;
    });

其中第一个函数处理成功,第二个函数处理失败,第三个函数无论成功失败都会先执行。

至此 Cairngorm 的全部功能都被 global.as 实现了。如果有盆友对我瞎掰的这些感兴趣,可以 下载 sample source 然后把你的想法告诉我。

Over :P

E4X 的另类应用

一直以来我们在 actionscript 中内嵌大段的多行文本时必须在每一号加上引号,然后再在结尾写加号。问题是一天三遍的写,太麻烦了。。。一直也没见过什么解决办法(除非写个外部文件载入,不过那个不爽,请忽略)。直到有一天。。。也就是昨天晚上,我一不小心想到了一个解决方案,大家请看:

1
2
3
4
5
6
7
8
9
10
11
12
13
var t:String=plain()(<!--
The Flash Player API classes are in the flash.* packages.
The Flash Player API refers to all packages, classes, functions,
properties, constants, events, and errors that are in the flash package.
They are unique to Flash Player, as opposed to the top-level classes,
such as Date, Math, and XML, or the language elements, which are based
on ECMAScript. The Flash Player API contains features that you expect
to find in object-oriented programming languages, such as the
flash.geom package for geometry classes, as well as features specific
to the needs of rich Internet applications, such as the flash.filters
package for expressiveness and the flash.net package for handling data
transmission to and from a server.
-->);

这个 plain 方法把代码中的一整段文本复给 t 变量,而字符串本身不用加引号!关键点就在 标识符,挖哈哈,这个 e4x 的应用还不错吧。

另外一个想到的应用是不用 Flex 的 embed 标签也可以内嵌图片等二进制元素到源码中,因为 Flex 的 embed 语法是不兼容 flash9 的,而且需要莫名其妙的引入 mx 包下的一些类。所以这个应用也是有一定用处地:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var b:String=plain(/W*/g)(<!--
89504e470d0a1a0a0000000d49484452
0000000e000000100803000000114abe
080000000467414d410000afc837058a
e90000001974455874536f6674776172
650041646f626520496d616765526561
647971c9653c0000000f504c5445ffff
ffff0000cccccc9999990000003952e0
8a000000314944415478da626046010c
cc0c08c0c4423a9791118261b28c8c20
1e4231988743165d2fb9ce6042022c0c
2c280020c0006dc501097dfe85550000
000049454e44ae426082
-->);

至于这一大堆乱码是什么意思,大家意会吧。。。

plain 方法的具体实现就不说了,很简单。有兴趣的朋友可以 在这里下载源码和实例 研究一蛤。

另外通过这个方法,我还发现了 as3 的编译器,以及 flex builder 2 的 as3 编辑器在词法分析上还是存在问题的。比如有时候我在 之间加入一整段类/包定义,编译器居然报错了。。。而且编辑器也很勤劳的加上语法高亮什么的。。。