工大后院

 找回密码
 加入后院

扫一扫,访问微社区

QQ登录

只需一步,快速开始

搜索
查看: 8796|回复: 9

[学习笔记]Flex框架Cairngorm Step by Step

[复制链接]
发表于 2008-5-9 20:49 | 显示全部楼层 |阅读模式
教程来自:
http://www.davidtucker.net/
E文
作者已经写了5步,这里是我学习过程中的体会
同时也算是一个简单的翻译吧。
重要说明 :本帖中绝大部分是个人学习笔记,很少有对原文的逐句翻译。(所以如果我写得比较乱希望不要以为原作者也是我这水平 - -||  www.gdutbbs.com)

本教程适合:
  • flex学习者
  • 有一定入门知识的对框架,设计模式的感兴趣的初学者
  • 看热闹者


如果你想一步一步编码实现下去你必需至少有:
  • Flex 3 SDK 或者 Flex 3 Builder (本教程不涉及如何用FlexSDK命令行编译和如何使用FlexBuilder)
  • 本教程涉及的框架的SWC文件(较小)


[ 本帖最后由 iptton 于 2008-5-10 14:18 编辑 ]
 楼主| 发表于 2008-5-9 20:50 | 显示全部楼层

Step1

第一步:Getting start
其实第一步与cairngorm框架的关系不大,只是一个简化版的MVC的实现方式

这里假设你已经对两个概念:普通WEB应用与RIA(Ajax)应用的不同有一定认识。

很大程度上可以说 RIA应用 是一种C/S 架构的应用,犹其从开发者的角度来看。
这时候,页面跳转时的数据共享就显得比较麻烦了(不懂flex的可以跳过了..)
比如,前一页面的的状态全局的某些数据状态
在普通web应用中一般是把这类数据放在session或cookies里
无论session还是cookies对客户来说都是唯一的。

在flash应用中如果要实现MVC,无疑Model就扮演着类似于session或cookies的角色
(或者我们可以直接就用cookies来实现model?嗯 ,这个想法在小型应用中应该可行,只是cookies不是对象,只支持字符串变量,不够方便。)

既然Model要是唯一的,这时我们很自然就能想到用单例(Singleton)模式。
之前我发过一张贴 as3的设计模式学习笔记 的帖。
由于as3不允许有private的构造函数,,在as3中实现singleton就有点曲折了:
package com.gdutbbs.iptton{
       public class Model{
             public static instance:Model;
             public function Model(force:InternalClass){
                     if (enforcer == null) {
                            //这句的作用:针对这样的调用:new MyModule(null)
                            //只能抛出异常,实际上新的实例还是被构造了!
                            throw new Error( "试图破坏单例模式?" );
                       }
             }
             public static function getInstance():Model{
                   if(instance==null){
                          instance=new Model(new InternalClass());
                   }
                   return instance;
             }
       }
}
// 单例的曲折实现..因为AS3不支持private的构造函数
// 原理:这里的SingletonEnforcer类只能在本文件内可见(internal)

class InternalClass{}


假设一个最常见的系统登陆,可以分为两个视图:一是登陆前,一是登陆成功后(当然,还可以细分为登陆失败)
可以分别给两个视图做成组件:分别在 views.loginForm 和 views.welcome
这里主程序的代码可能是这样
这里用了viewStack。(如果你学过swing的对viewStack应该不陌生)
简单点说viewStack就是一个tabView处在viewStack里的每个组件同一时间只能显示一个。
显示哪一个由viewStack的selectedIndex指定。
学过flex的应该能想到绑定binding这个概念,
这里用的就是绑定了,viewStack的selectedIndex通过对MyModel的属性currentState绑定来实现来获取当前状态以便决定当前应当显示哪个视图。
main.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
    xmlns:v="views.*"
    >
    <mx:Script>
        <![CDATA[
            import Models.MyModel;
            [Bindable]
            public var m:MyModel=MyModel.getInstance();
        ]]>
    </mx:Script>
    <mx:ViewStack selectedIndex="{m.currentState}" width="100%" height="100%">
        <v:loginForm width="100%" height="100%"/>
        <v:welcome width="100%" height="100%"/>
    </mx:ViewStack>
</mx:Application>


接下来是两个视图对model:
models/welcome.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
        <mx:Script>
                <![CDATA[
                        import Models.MyModel;
                        public var m:MyModel=MyModel.getInstance();
                ]]>        
        </mx:Script>               
        <mx:VBox>
                <mx:Label fontSize="20" text="WELCOME!!!欢迎光临!!" />
                <mx:Button click="{m.currentState=MyModel.LOGIN}" label="LOGOUT>>退出" />
        </mx:VBox>
</mx:Canvas>

models/loginForm.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="horizontal">
    <mx:Script>
                <![CDATA[
                        import Models.MyModel;
                        public var m:MyModel=MyModel.getInstance();
                ]]>      
    </mx:Script>
    <mx:Form>
        <mx:FormItem label="userName用户名:">
            <mx:TextInput id="ti_uname" />
        </mx:FormItem>
        <mx:FormItem label="password密码:">
            <mx:TextInput displayAsPassword="true" id="ti_pwd" />
        </mx:FormItem>
        <mx:FormItem label="">
            <mx:Button label="登陆LOGIN" id="bt_submit" click="{m.currentState=MyModel.LOGIN}"/>
        </mx:FormItem>
    </mx:Form>
</mx:TitleWindow>

models/MyModel.as

package Models{
    import com.adobe.cairngorm.model.IModelLocator;
    [Bindable]
    public class MyModel implements IModelLocator{
        // 保存实例的变量
        private static var instance:MyModel;
        public function MyModel(enforcer:SingletonEnforcer) {
            if (enforcer == null) {
                //这句的作用:防止这样的调用:new MyModule(null)
                //只能抛出异常,实际上新的实例还是被构造了!
                throw new Error( "You Can Only Have One ModelLocator" );
            }
        }
        // 返回实例
        public static function getInstance() : MyModel {
            if (instance == null) {
                instance = new MyModel( new SingletonEnforcer );
            }
            return instance;
        }

        public var currentState:uint=0;
        
        //以下常量的做法实际上并不好,与主程序的布局顺序相关联了!
        public static const LOGIN:uint=0;
        public static const WELCOME:uint=1;
    }
}
// 单例的曲折实现..因为AS3不支持private的构造函数
// 原理:这里的SingletonEnforcer类只能在本文件内可见(internal)
class SingletonEnforcer {}



=====================================================================================
收获:
在学这个之前对多视图跳转时的状态保存
我的方法是在主程序里加一个公共变量(相当于全局变量)
然后在每个组件里通过 Application.application.globalVariable来引用
这样的最大不好之处我个人认为是组件与主程序间的藕合太密切了。
尽管知道singleton也在java课程设计中使用过但时当时还是没想到这个可以这样做
当然,本例中各视图还是离不开model这个约束,如何解除这个藕合?下一帖和你说..

[ 本帖最后由 iptton 于 2008-5-9 22:49 编辑 ]
回复

使用道具 举报

 楼主| 发表于 2008-5-9 20:56 | 显示全部楼层

step2

第二步。。。。
原文只说了两件事:viewStack中的selectedIndex和用常量消除“魔数"(magic number)。
本帖中第一步已有说明,对于魔数的处理属于编程素质的问题,这里就不讨论了。。

[ 本帖最后由 iptton 于 2008-5-9 22:22 编辑 ]
回复

使用道具 举报

 楼主| 发表于 2008-5-9 20:56 | 显示全部楼层

step3

第三步。。。
现在MVC有了Model 和 View 只欠一个Controller了
这一步的内容包括:
1,Event
2,FrontController
3,Command

事件模型在flash里占有很大份量,相关内容可以看看这里,如果看不明白建议翻下相关文档(flexbuilder自带文档就很不错)


原网站上对MVC的描述:


cairngormdiagram-basic.jpg

有了对“事件”,view就可以轻松解除对model的依赖了
当然,如果你了解了什么是事件,想必对观察者(observer)模式不会陌生


当登陆按钮被点击时,发布LoginEvent事件。
models/loginForm.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="horizontal">
    <mx:Script>
        <![CDATA[
            import Models.*;
            import events.*;
            private var m:MyModel=MyModel.getInstance();
            private function login():void{
                var u:String=ti_uname.text;
                var p:String=ti_pwd.text;
                //这里可以做一些如检查输入是否为空的操作然后再触发事件
                //本例从简

                var le:LoginEvent=new LoginEvent(u,p);
                le.dispatch();//这里的事件分发形式有点奇特。。
            }
        ]]>
    </mx:Script>
    <mx:Form>
        <mx:FormItem label="userName用户名:">
            <mx:TextInput id="ti_uname" />
        </mx:FormItem>
        <mx:FormItem label="password密码:">
            <mx:TextInput displayAsPassword="true" id="ti_pwd" />
        </mx:FormItem>
        <mx:FormItem label="">
            <mx:Button label="登陆LOGIN" id="bt_submit" click="login()"/>
        </mx:FormItem>
    </mx:Form>
</mx:TitleWindow>


再来看看LoginEvent和FrontController:
events/LoginEvent.as

package events{
        import com.adobe.cairngorm.control.CairngormEvent;
        import flash.events.Event;

        public class LoginEvent extends CairngormEvent{
                //事件的名称
                public static const LOGIN:String="LOGIN";
                public var username:String;
                public var passwd:String;
                public function LoginEvent(
                        submittedUsername:String,
                        submittedPassword:String,
                        bubbles:Boolean=false,
                        cancelable:Boolean=false
                        ){
                        this.username=submittedUsername;
                        this.passwd=submittedPassword;
                        super(LOGIN, bubbles, cancelable);
                }
                //因为添加了username和password两个属性,如果用原来的clone函数,会丢掉这两个参数
                override public function clone():Event{
                        return new LoginEvent(username,passwd);
                }
        }
}


一般意义上的MVC中C负责的是逻辑的处理,而FrontControl这种方式
则是把逻辑方面的事分给了Command (这里是否就是传说中的Command模式?)

FrontController只是事件与Command之关的传话筒。
(据我所知的PHP框架都是用这种MVC,当然,在PHP这种层面应用中,“事件”就是用户的请求)

control/MyController.as
package control{
        import com.adobe.cairngorm.control.FrontController;
        import commands.*;
        import events.*;

        public class MyController extends FrontController{
                public function MyController(){
                        super();
                        //绑定登陆事件与登陆处理逻辑
                        //是不是很像 addEventListener(someEvent, someCallBack ) ?
                        //只不过不同的是这里的callBack是class
                        //在事件触发时才被实例化并肯执行其excute函数

                        this.addCommand(LoginEvent.LOGIN,LoginCommand);
                }
        }
}


对登陆的逻辑处理:
commands/LoginCommand.as

package commands{
        import Models.*;
       
        import com.adobe.cairngorm.commands.ICommand;
        import com.adobe.cairngorm.control.CairngormEvent;
        import events.*;
       
        public class LoginCommand implements ICommand{
                public var m:MyModel=MyModel.getInstance();
                public function LoginCommand(){
                        //所有的Command类都是工厂模式中被构造?
                }

                //被动调用,这个是vistor模式还是Observe模式?
                public function execute(event:CairngormEvent):void{
                        var le:LoginEvent = event as LoginEvent;
                        if( this._checkLogin(le.username,le.passwd) ){
                                //登陆成功
                                //进入欢迎界面

                                m.currentState=MyModel.WELCOME;
                        }else{
                                //发布登陆失败事件

                        }
                       
                }
                private function _checkLogin(u:String,p:String):Boolean{
                        //这里应该有对主机的请求,下回说明,这里只作一个简单的模拟
                        if(u=="a" && p=="a"){
                                return true;
                        }
                        return false;
                }
               
        }
}


在主程序里引入FrontController:
main.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
        xmlns:v="views.*"
        xmlns:c="control.*"
        >
        <mx:Script>
                <![CDATA[
                        import Models.MyModel;
                        [Bindable]
                        public var m:MyModel=MyModel.getInstance();
                ]]>
        </mx:Script>
        <c:MyController id="controller" />
        <mx:ViewStack selectedIndex="{m.currentState}" width="100%" height="100%">
                <v:loginForm width="100%" height="100%"/>
                <v:welcome width="100%" height="100%"/>
        </mx:ViewStack>
</mx:Application>


=======================================================================
收获:
加深了对FrontController的理解
另外,对如何使用框架有了新的想法,
有这样一句话:

  1. the best solution is not when there is nothing left to add, but when there is nothing left to take away!
  2. 最好的解决方案不是无物可加,而是无物可减!
复制代码

[ 本帖最后由 iptton 于 2008-5-9 23:07 编辑 ]
回复

使用道具 举报

 楼主| 发表于 2008-5-9 20:56 | 显示全部楼层

step4: 与服务端交互

第四步:这一步新的概念比较多
1。 ServiceLocator 可以联系ModelLocator,看成远程数据的Model,一般有三种:HTTPService, WebService, 或RemoteObjec
2。Business Delegate  是Command与ServiceLocator的桥梁。有三个功能:定位相应的远程服务,调用该服务,返回该服务的结果。通常每个Service都会有一个业务代理。
3。 Value Object - 只有属性的类,可以看成服务端与客户端数据的公共接口。

使用了以上三个新事物后的事件流:
cairngormdiagramStep4.jpg

流程就不多解释了,直接看代码:
在上一步,Command把 Delegate=>sevices=>ServerProcess=>services=>Delegate=>Response 这些工作全模拟完成了。
现在我们可以把焦点放在Command模拟的那部分:

不管Delegate怎么实现,现在Command应该有两个职能:提交远程请求的参数及对远程响应进行处理。
在Flex的rpc里,响应的接口是 IResponse,所以新的LoginCommand应该实现 ICommand和IResponse两个接口。

commands/LoginCommand.as

package commands{
        import mx.rpc.IResponder;
        import com.adobe.cairngorm.commands.ICommand;
        import com.adobe.cairngorm.control.CairngormEvent;
        import mx.controls.Alert;
        import events.*;
        import Models.*;
        import business.*;//LoginDelegate;
       
        public class LoginCommand implements ICommand,IResponder{
                public var m:MyModel=MyModel.getInstance();
                public function LoginCommand(){
                        //所有的Command类都是工厂模式中被构造?
                }

                //被动调用,这个是vistor模式还是Observe模式?
                public function execute(event:CairngormEvent):void{
                        var le:LoginEvent = event as LoginEvent;
                        var delegate:LoginDelegate = new LoginDelegate( this );
                        delegate.login(loginEvent.loginAttempt);

                        /*
                        if( this._checkLogin(le.username,le.passwd) ){
                                //登陆成功
                                //进入欢迎界面
                                m.currentState=MyModel.WELCOME;
                        }else{
                                //发布登陆失败事件
                                //m.currentState=MyModel.LOGIN;
                        }*/

                       
                }
                /*
                private function _checkLogin(u:String,p:String):Boolean{
                        if(u=="a" && p=="a"){
                                return true;
                        }
                        return false;
                }*/
               
                //实现IResponse的两个接口

                public function result( event:Object ):void {
                        if(event.result == true) {
                                m.currentState = MyModel.WELCOME;
                        } else {
                                mx.controls.Alert.show("Password Incorrect","ERROR");
                        }
                }
                public function fault( event:Object ):void {
                        mx.controls.Alert.show("远程服务出错!");
                }
               
        }
}

loginService
<?xml version="1.0" encoding="utf-8"?>
<cairngorm:ServiceLocator
        xmlns:cairngorm="com.adobe.cairngorm.business.*"
        xmlns:mx="http://www.adobe.com/2006/mxml">
        <!-- Login Service -->
        <mx:RemoteObject id="loginService"
                showBusyCursor="true"
                destination="ColdFusion"
                source="CairngormTest.CairngormLogin">
                <mx:method name="login" />
        </mx:RemoteObject>
</cairngorm:ServiceLocator>

[ 本帖最后由 iptton 于 2008-5-9 23:56 编辑 ]
回复

使用道具 举报

 楼主| 发表于 2008-5-9 20:57 | 显示全部楼层

step5

第五步
回复

使用道具 举报

发表于 2008-5-9 22:28 | 显示全部楼层
以前做flex~~好过瘾 。。60个控件要背熟。。。现在做C#混合java,亦好过瘾。。
回复

使用道具 举报

 楼主| 发表于 2008-5-10 03:02 | 显示全部楼层
我只用来做过软件工程课程设计,边做边查API。。。
LS是做商业项目吗,背熟控件的API。。。比较。。。
回复

使用道具 举报

 楼主| 发表于 2008-5-10 03:03 | 显示全部楼层
又要重拾AMFPHP了。。。数据库课程设计。。。
回复

使用道具 举报

发表于 2008-5-10 10:31 | 显示全部楼层
对啊。。是商业项目。。soap那类。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 加入后院

本版积分规则

QQ|Archiver|手机版|小黑屋|广告业务Q|工大后院 ( 粤ICP备10013660号 )

GMT+8, 2024-5-1 05:01

Powered by Discuz! X3.5

Copyright © 2001-2024 Tencent Cloud.

快速回复 返回顶部 返回列表