2014年4月27日 星期日

MVC,MVP,MVVM,MVFM 的比較和架構


今天因為在想一些架構上的問題 !

所以就找了一些東西看!

先講一下我看完一些資料後我自己的了解!

Model-View-Controller

的整個流程是

View <-> Controller -> Model -> View

View 被使用者操作 和 cotroller 互相作用 !

如果有 Cotroller 會通知 Model 作一些處理

Model 會通知 View 作一些改變!

為了將 循環 切斷 將切分得更乾淨!

產生了 Model - View - Presenter

Model <-> Presenter <-> View

View 和 Model 完全的切開了 !

但是更新資料卻要透過 Presenter  傳遞 !

基於 MVP 的架構 又多了一種拆分方式!

Model-View-ViewModel

這是一種特化的 MVP 拆分方式!

針對 View 去拆分 Presenter

讓 View 的流程更順 ~~

Model -> ViewModel <-> View

因為針對 View 去拆分 所以 ViewModel 重用的可能性變得很低

所以出現了

Model - View- FunctionModel 的拆分方法

把 針對 View 拆分的方式 改成對一個 Function 去拆分!

這樣會拆分得更細! 重用的機會也就更多!

以上大概就是我讀了下面那樣多篇的一些粗淺心得!

//==================================================

Riot.js — 1Kb 大小的 JavaScript 的 MVP 框架
http://www.oschina.net/translate/riotjs-the-1kb-mvp-framework

MVC和MVP的一些思考
http://www.cnblogs.com/janyou/archive/2008/12/05/1348244.html

Scaling Isomorphic Javascript Code
http://blog.nodejitsu.com/scaling-isomorphic-javascript-code/

[PureMVC]初學者入門教學Part.1 Application與Facde
http://www.ria.tw/2010/08/puremvcpart1-applicationfacde.html

Understanding MVC And MVP (For JavaScript And Backbone Developers)
http://addyosmani.com/blog/understanding-mvc-and-mvp-for-javascript-and-backbone-developers/

MVFM vs MVVM 的分析 比較
http://www.devdiv.com/mvfm_-blog-55433-50285.html

框架設計! 使用分析過程!
http://my.eoe.cn/961719/articles

Python里没有接口,如何写设计模式?
http://www.zhihu.com/question/20685467

充血模型与贫血模型分别适用于何种情况?
http://www.zhihu.com/question/20360521

如何区分javascript设计模式中的中介者模式(Mediator Pattern)与观察者模式(Observer Pattern)?
http://www.zhihu.com/question/21092827

MVC MVP MVVM MVFM 等的分析比較!
http://www.cnblogs.com/indream/p/3602348.html


http://seldo.com/weblog/2011/06/15/orm_is_an_antipattern

http://www.xuebuyuan.com/1230694.html

再抽象一点
http://techsingular.net/?p=1125

2014年4月26日 星期六

[JS] 嘗試 製作簡單的 視差效果!

前一段時間 很流行的 視差效果!

終於有花一小段時間 弄了一個簡單的版本!

雖然是使用別人的類別作的

不過大致的瞭解了其中的技術是怎樣做的

使用了一些 視差效果 的套件

幾乎每一款都有各自的特點!

使用方法也都不同

但是我最後決定使用的是 skrollr 這一套

因為他的使用方法簡單 和 API 相當的友善 看過 demo 後就會用 9 成

不過讓我決定使用這一套工具的原因有下面幾點

1.  API 簡單
2. 手機可以使用
3. 基本需求都可以達成
4. 到現在還有在更新
5. 說明明白扼要
6. 體積小!

經過實機測試 在我手機的瀏覽器 可以使用

和我測試的其他的 API 有所不同 !

有一些在手機上是會有問題的

測試網頁

基本上和原本的 demo 是差不多的 不過用我自己的方式詮釋一次!


參考資料:
http://stephband.info/jparallax/
http://joydesign.coletree.com/2012/07/1562/
https://github.com/Prinzhorn/skrollr
http://www.webhek.com/misc/parallax/
http://www.mrmu.com.tw/2011/10/19/parallax-scrolling/
http://webdesignledger.com/inspiration/21-examples-of-parallax-scrolling-in-web-design
http://rettamkrad.blogspot.tw/2013/05/blog-post.html

2014年4月21日 星期一

[as] 使用 Ant 編譯並測試 我自己的 unit test

繼續上一篇 我自己彷 flex unit 作的 unit test

其實還有一個需求是可以整合 Jenkins 等

可以自動的 編譯 測試  整合 發現錯誤 !

講講我遇到的幾個問題點!

1. 如何在 command line 下執行 swf ?

ans :
因為我知道有 adl, fdb 這兩樣可以 command line 測試 swf or air 的程式
為了簡單起見, 選擇使用 fdb 去測試 !
雖然我覺得使用 adl 去測試會是比較好的選項
因為 air 可以直接輸出檔案 ! 更便於報表執行 等等
不過在我心裡想 那是最後必殺技..
所以接下去的問題都將由 fdb 去做考慮.

2.  如何使用 ant 自動輸入文字讓 fdb 可以執行

ans:
其實這個問題 卡最久 ! 
不過後來看 exec task 時發現有一個 input 和 inputstring
我就利用了 inputstring 去輸入文字
不過問題在於 只能輸入一種!
如果要再輸入其他的 例如 quit , yes 等
好像沒辦法 !

3.  如何停止並關閉 fdb

ans:
這個部分 有繼續翻 task 或是 fdb 相關的部分
想說是不是有 類似的東西 可以輸入 或是 去停止的!
不過 在沒發現太多可行的方法
所以我再一次的修正了我的 BasicUnitTestCase
讓 swf 去執行 System.exit(0); 去結束 fdb

4. 如何 output 內容 ?

ans:
目前所說的 output 的部分
只是簡單的再 ant 上trace出來
還沒有整合到 Jenkins 去判斷結果的部分
之前嘗試的幾種方式 cmd, bat 等等
都會另外開啟令一個視窗 結果 ant 就結束了!
不過後來就改成直接執行 fdb 後就可以 trace 了 !


下面就是我用來測試 swf 的 基本版 task 
      <target name="open player">
            <echo>Launching '${DEPLOY_DIR}/unitTestDemo.swf'...</echo>
            <exec executable="fdb" failonerror="false" inputstring="continue"  >
                <arg line="run '${DEPLOY_DIR}/unitTestDemo.swf'" />
            </exec>
       </target>


下面是我的測試圖片!



我參考的幾篇文章
http://anycent.blog.163.com/blog/static/84498427201022335056341/
https://ant.apache.org/manual/Tasks/exec.html
http://forums.adobe.com/message/3385890#3385890#3385890

2014年4月19日 星期六

[AS] Unit Test 的基本測試工具

因為不是每個 Flash IDE 都有簡單的 flex unit 可以使用!

所以我仿照 flex unit 作了一個自己的測試類別 BasicUnitTestCase

只要繼承這個類別

就可以仿照 flex unit 4 的方式來建構 test case

並且簡單的測試!

不需要使用 premium 版本的 IDE

也不需要特定的 IDE 等等的部分!

當然也有一些別人寫好的這種類別

例如 asUnit asUnit Git URL

因為某些原因所以  asunit 對我來說不是很適用,

所以才產出了這個類別 !

來講一下我寫這個 測試類別的基本需求

1. 可以直接和 flex unit 交互使用(test case, Assert ).
2. 提供簡易報表 和 詳細報表
3. 簡單的使用和測試.

因為需求 1,

所以有了 metadata 的分析

和 flex unit 一樣直接取 Test tag 的 test case

也和 flex unit 一樣的基本斷言函式 assertEquals

為了測試正確 所以暫時不使用 flex unit 的 Before 和 After 這兩個 metadata,

強迫使用者複寫 startUp 和 shutdown 這兩個函數

確保一定會做到這兩個動作!

因為需求 2.

整個測試完畢會使用 showLog 將基本的報告印出.

也可以使用 Detail 來取的比較完整的報告

最後需求3.

只需要 BasicUnitTestCase 就可以進行測試,

雖然目前功能還是相對簡單! 

所以對目前的幾個 IDE 介面都可以使用.

綜合以上 有了下面的類別
package com.tenchiwang.unitTest
{
    import flash.events.ErrorEvent;
    import flash.utils.describeType;

    public class BasicUnitTestCase
    {
        private var _testCaseList:Vector.<TestCaseVo>;
        private var _detail:String = '';
        private var _ErrorCount:int;
        private var _PassCount :int;
        private var _isPass:Boolean = false;
        private var _callback:Function;
       
        // 基本的測試類別!
        public function BasicUnitTestCase(callBack:Function = null)
        {
            _callback = callBack;
            _testCaseList = new Vector.<TestCaseVo>();
           
            saveDetail('Analyze Unit Test....');
           
            var accessorsList:XMLList = describeType(this).method;
            var fun:Function;
            var vo:TestCaseVo;
            var acces:XML;
            var metadataName:String;
           
            for(var i:int = 0;i<accessorsList.length();i++)
            {
                acces = accessorsList[i];
                metadataName = acces.metadata[0].@name;
                metadataName = metadataName.toLocaleLowerCase();
               
                switch(metadataName)
                {
                    case 'test':
                        fun = this[acces['@name']] as Function;
                       
                        if(fun != null)
                        {
                            vo = new TestCaseVo();
                            vo.name = acces['@name'];
                            vo.fun = fun;
                             _testCaseList.push(vo);
                        }
                        break;
                   
                    default:
                }
            }
           
            saveDetail('Analyze Unit Test End. Get ' + _testCaseList.length + ' tests');
            //
            runTest();
           
            //
            if(_callback != null)
                _callback.apply();
        }
       
        final public function assertEquals(a:*, b:*):void
        {
            if( a === b)
            {
                _PassCount++;
                _isPass = true;
            }
        }
       
        private function runTest():void
        {
            showLog('Unit Test Start.');
           
            saveDetail('Unit Test Start ....');
            saveDetail('=======================');
            var len:int = _testCaseList.length;
            var vo:TestCaseVo;
            for(var i:int = 0; i < len ;i++)
            {
                vo = _testCaseList[i];
               
                saveDetail( vo.name + ' Start.' );
               
                _isPass = false;
                startUp();
               
                try
                {
                    vo.fun.call();
                }
                catch(error:Error)
                {
                    showLog('Test Case ' + vo.name + ' Error.');
                    saveDetail('Test Case ' + vo.name + ' Error.');
                    _ErrorCount++;
                }
                catch(evt:ErrorEvent)
                {
                    showLog('Test Case ' + vo.name + ' Error.');
                    saveDetail('Test Case ' + vo.name + ' Error.');
                    _ErrorCount++;
                }

                shutdown();
                saveDetail(vo.name + ' End. ' );
            }
            showLog('Unit Test End.');
           
            showLog('Test Case ' + len);
            showLog('Pass Case '  + _PassCount );
            showLog('Error Case ' + _ErrorCount);
           
           
            //
            saveDetail('=======================');
            saveDetail('Unit Test End.');
           
            saveDetail('Test Case ' + len);
            saveDetail('Pass Case '  + _PassCount );
            saveDetail('Error Case ' + _ErrorCount);
           
        }
       
        private function saveDetail(msg:String):void
        {

           
            _detail += getDate() + msg + '\n';
        }
       
        private function getDate():String
        {
            var date:Date = new Date();
            var dateStr:String = '[' + date.getUTCHours() + ' : ' + date.getUTCMinutes() + ' : ' +
                date.getUTCSeconds() + ' ' + date.getUTCMilliseconds() +'] ' ;
           
            return dateStr;
        }
       
        /**
         *  每次 unit test 前須要做的事
         */
        protected function startUp():void
        {
            throw new Error('Plz override startUp.');
        }
       
        /**
         *  每次 unit test 後須要做的事
         */
        protected function shutdown():void
        {
            throw new Error('Plz override shutdown.');
        }
       
        /**
         * 預設的顯示 Log 的通用函數
         * @param msg
         *
         */       
        protected function showLog(msg:String):void
        {
            trace(getDate() + msg);
        }
       
        /**
         * 顯示詳細的測試資料.
         */
        public function get Detail():String
        {
            return _detail;
        }
       
    }
}

internal class TestCaseVo
{
    public var name:String;
    public var fun:Function;
}

[AS] 簡易的事件機制的單元測試


上一篇 介紹了一個 簡單的事件機制類別

主要的需求是
1. 可以註冊監聽事件.
2. 可以移除單一事件
3. 可以移除全部的事件
4. 可以傳遞參數
5. 不同於 flash 基本的事件機制.

下面我基於這幾個需求寫了一些單元測試!

1. 可以註冊監聽事件.

        [Test]
        public function testSendEvent():void
        {
            EventManager.getInstancet().addListener(TEST_SEND_EVENT, _mock.handler);
            EventManager.getInstancet().sendEvent(TEST_SEND_EVENT);
            assertEquals(_isGetEvent, true);
        }


2. 可以移除單一事件
        [Test]
        public function testRemoveEvent():void
        {
            EventManager.getInstancet().addListener(TEST_REMOVE_EVENT, _mock.handler);
            EventManager.getInstancet().removeListener(TEST_REMOVE_EVENT, _mock.handler);
            EventManager.getInstancet().sendEvent(TEST_REMOVE_EVENT);
            assertEquals(_isGetEvent, false);
        }


3. 可以移除全部的事件
        [test]
        public function testRemoveEventAllListener():void
        {
            var EVENT_ONE:String = 'EVENT_ONE';
            var EVENT_TWO:String = 'EVENT_TWO';
           
            _mock.eventOneCount = 0;
            _mock.eventOnTwoCount = 0;
           
            _mock.handlerEvent1 = function():void
            {
                _mock.eventOneCount++;
            };
            _mock.handlerEvent2 = function():void
            {
                _mock.eventOneCount++;
            };
            _mock.handlerEvent3 = function():void
            {
                _mock.eventOneCount++;
            };
            _mock.handlerEvent4 = function():void
            {
                _mock.eventOnTwoCount++;
            };
           
            EventManager.getInstancet().addListener(EVENT_ONE, _mock.handlerEvent1);
            EventManager.getInstancet().addListener(EVENT_ONE, _mock.handlerEvent2);
            EventManager.getInstancet().addListener(EVENT_ONE, _mock.handlerEvent3);
            EventManager.getInstancet().addListener(EVENT_ONE, _mock.handlerEvent4);
           
            EventManager.getInstancet().addListener(EVENT_TWO, _mock.handlerEvent4);
           
            EventManager.getInstancet().removeListenerAll(EVENT_ONE);
           
            EventManager.getInstancet().sendEvent(EVENT_ONE);
            EventManager.getInstancet().sendEvent(EVENT_TWO);
           
            if(_mock.eventOneCount == 0 && _mock.eventOnTwoCount == 1)
                assertEquals(true, true);
        }


4.可以傳遞參數
        [test]
        public function testSendParmas():void
           
        {
            EventManager.getInstancet().addListener(TEST_PARAMS_EVENT, _mock.handlerParams);
            EventManager.getInstancet().sendEvent(TEST_PARAMS_EVENT, TEST_MSG, TEST_MSG2);
        }


5. 不同於 flash 基本的事件機制.
EventManager.getInstancet().addListener(eventType:String, handler:Function);
EventManager.getInstancet().sendEvent(eventType:String, ...args);


所以主要的需求都有相關的 test case,

然後針對一些這個類別使用上的問題,

也都有相關的 test case 讓使用者可以了解一些使用的限制,

像是可以註冊兩個一樣事件 一樣監聽函數

發送一次事件可以執行兩次! 移除時也只會移除一個

監聽事件的執行的位置! 傳送的參數有差別時會出現那些錯誤!

監聽函數的執行順序, 傳入空監聽的情況,

各種使用上的特殊情況, 正常情況都記錄起來

未來有需求在修改這個類別後

可以在進行一次這些測試! 

用來證明以前的需求, 在修改後不會產生錯誤!

下面是全的 test case 的 code
package
{
    import com.tenchiwang.manager.EventManager;
    import com.tenchiwang.unitTest.BasicUnitTestCase;
   
    public class demo3 extends BasicUnitTestCase
    {
       
        private var _isGetEvent:Boolean = false;
        private var _mock:Object;
       
        private const TEST_SEND_EVENT:String      = 'TEST_SEND_EVENT';
        private const TEST_REMOVE_EVENT:String    = 'TEST_REMOVE_EVENT';
        private const TEST_PARAM_EVENT:String     = 'TEST_PARAM_EVENT';
        private const TEST_PARAMS_EVENT:String    = 'TEST_PARAMS_EVENT';
        private const TEST_CURRENT_REMOVE:String  = 'TEST_CURRENT_REMOVE';
        private const TEST_ORDER_EVENT:String     = 'TEST_ORDER_EVENT';
       
        private const TEST_MSG:String  = 'TEST_MSG';
        private const TEST_MSG2:String = 'TEST_MSG2';
       
        //============== 正常案例   && 邊界案例
        [Test]
        public function testSendEvent():void
        {
            EventManager.getInstancet().addListener(TEST_SEND_EVENT, _mock.handler);
            EventManager.getInstancet().sendEvent(TEST_SEND_EVENT);
            assertEquals(_isGetEvent, true);
        }
       
        [Test]
        public function testRemoveEvent():void
        {
            EventManager.getInstancet().addListener(TEST_REMOVE_EVENT, _mock.handler);
            EventManager.getInstancet().removeListener(TEST_REMOVE_EVENT, _mock.handler);
            EventManager.getInstancet().sendEvent(TEST_REMOVE_EVENT);
            assertEquals(_isGetEvent, false);
        }
       
        [test]
        public function testSendParma():void
        {
            EventManager.getInstancet().addListener(TEST_PARAM_EVENT, _mock.handlerParam);
            EventManager.getInstancet().sendEvent(TEST_PARAM_EVENT, TEST_MSG);
        }
       
        [test]
        public function testSendParmas():void
           
        {
            EventManager.getInstancet().addListener(TEST_PARAMS_EVENT, _mock.handlerParams);
            EventManager.getInstancet().sendEvent(TEST_PARAMS_EVENT, TEST_MSG, TEST_MSG2);
        }
       
        [test]
        public function testCurrentRemove():void
        {
            EventManager.getInstancet().addListener(TEST_CURRENT_REMOVE, _mock.handlerCurrent);
            EventManager.getInstancet().addListener(TEST_CURRENT_REMOVE, _mock.handlerRemoveCurrent);
           
            EventManager.getInstancet().removeListener(TEST_CURRENT_REMOVE, _mock.handlerRemoveCurrent);
           
            EventManager.getInstancet().sendEvent(TEST_CURRENT_REMOVE);
           
            assertEquals(_isGetEvent, true);
        }
       
        [test]
        public function testCurrentRemove2():void
        {
            _mock.handlerCurrent = function():void
            {
                _isGetEvent  = false;
            }
           
            _mock.handlerRemoveCurrent = function():void
            {
                _isGetEvent = true;
            }
           
            EventManager.getInstancet().addListener(TEST_CURRENT_REMOVE, _mock.handlerCurrent);
            EventManager.getInstancet().addListener(TEST_CURRENT_REMOVE, _mock.handlerRemoveCurrent);
           
            EventManager.getInstancet().removeListener(TEST_CURRENT_REMOVE, _mock.handlerCurrent);
           
            EventManager.getInstancet().sendEvent(TEST_CURRENT_REMOVE);
           
            assertEquals(_isGetEvent, true);
        }
       
        [test]
        public function testOrder():void
        {
            _mock.order = 0;
            _mock.handlerOrder1 = function():void
            {
                if(_mock.order == 0)
                    _mock.order = 1;
            };
               
            _mock.handlerOrder2 = function():void
            {
                if(_mock.order == 1)
                    _mock.order = 2;
            };
           
            EventManager.getInstancet().addListener(TEST_ORDER_EVENT, _mock.handlerOrder1);
            EventManager.getInstancet().addListener(TEST_ORDER_EVENT, _mock.handlerOrder2);
           
            EventManager.getInstancet().sendEvent(TEST_ORDER_EVENT);
           
            assertEquals(_mock.order, 2);
        }
       
        [test]
        public function testDouble():void
        {
            var TEST_DOUBLE_EVENT:String = 'TEST_DOUBLE_EVENT';
           
            _mock.count = 0;
            _mock.handlerDouble = function():void
            {
                _mock.count++;
            };
           
           
            EventManager.getInstancet().addListener(TEST_DOUBLE_EVENT, _mock.handlerDouble);
            EventManager.getInstancet().addListener(TEST_DOUBLE_EVENT, _mock.handlerDouble);
           
            EventManager.getInstancet().sendEvent(TEST_DOUBLE_EVENT);
           
            assertEquals(_mock.count, 2);
        }
       
        [test]
        public function testRemoveDouble():void
        {
            var TEST_REMOVE_DOUBLE_EVENT:String = 'TEST_REMOVE_DOUBLE_EVENT';
           
            _mock.count = 0;
            _mock.handlerDouble = function():void
            {
                _mock.count++;
            };
           
            EventManager.getInstancet().addListener(TEST_REMOVE_DOUBLE_EVENT, _mock.handlerDouble);
            EventManager.getInstancet().addListener(TEST_REMOVE_DOUBLE_EVENT, _mock.handlerDouble);
           
            EventManager.getInstancet().removeListener(TEST_REMOVE_DOUBLE_EVENT, _mock.handlerDouble);
           
            EventManager.getInstancet().sendEvent(TEST_REMOVE_DOUBLE_EVENT);
           
            assertEquals(_mock.count, 1);
        }
       
        [test]
        public function testRemoveEventAllListener():void
        {
            var EVENT_ONE:String = 'EVENT_ONE';
            var EVENT_TWO:String = 'EVENT_TWO';
           
            _mock.eventOneCount = 0;
            _mock.eventOnTwoCount = 0;
           
            _mock.handlerEvent1 = function():void
            {
                _mock.eventOneCount++;
            };
            _mock.handlerEvent2 = function():void
            {
                _mock.eventOneCount++;
            };
            _mock.handlerEvent3 = function():void
            {
                _mock.eventOneCount++;
            };
            _mock.handlerEvent4 = function():void
            {
                _mock.eventOnTwoCount++;
            };
           
            EventManager.getInstancet().addListener(EVENT_ONE, _mock.handlerEvent1);
            EventManager.getInstancet().addListener(EVENT_ONE, _mock.handlerEvent2);
            EventManager.getInstancet().addListener(EVENT_ONE, _mock.handlerEvent3);
            EventManager.getInstancet().addListener(EVENT_ONE, _mock.handlerEvent4);
           
            EventManager.getInstancet().addListener(EVENT_TWO, _mock.handlerEvent4);
           
            EventManager.getInstancet().removeListenerAll(EVENT_ONE);
           
            EventManager.getInstancet().sendEvent(EVENT_ONE);
            EventManager.getInstancet().sendEvent(EVENT_TWO);
           
            if(_mock.eventOneCount == 0 && _mock.eventOnTwoCount == 1)
                assertEquals(true, true);
        }
       
        //================ 錯誤案例!
        [test]
        public function testMoreParamError():void
        {
            var TEST_MORE_PARAMS:String = 'TEST_MORE_PARAMS';
           
            _mock.count = 0;
            _mock.handler2 = function(msg:String):void
            {
                if(msg == TEST_MSG)
                    _mock.count++;
            };
           
            EventManager.getInstancet().addListener(TEST_MORE_PARAMS, _mock.handler2);
            try
            {
                EventManager.getInstancet().sendEvent(TEST_MORE_PARAMS, TEST_MSG, TEST_MSG2);               
            }
            catch(error:Error)
            {
                assertEquals(_mock.count, 0);
            }
           
        }
       
        [test]
        public function testlessParamError():void
        {
            var TEST_LESS_PARAMS:String = 'TEST_LESS_PARAMS';
           
            _mock.count = 0;
            _mock.handler2 = function(msg:String, msg2:String):void
            {
                _mock.count++;
            };
           
            EventManager.getInstancet().addListener(TEST_LESS_PARAMS, _mock.handler2);
            try
            {
                EventManager.getInstancet().sendEvent(TEST_LESS_PARAMS, TEST_MSG);               
            }
            catch(error:Error)
            {
                assertEquals(_mock.count, 0);
            }
           
        }
       
        //================ 特殊案例
       
        [test]
        public function testFunctionLocal():void
        {
            var TEST_LOCAL:String = 'TEST_LOCAL';
           
            _mock.handler = function(msg:String):void
            {
                if(this == EventManager.getInstancet())
                    assertEquals(true, true);
            };
           
            EventManager.getInstancet().addListener(TEST_LOCAL, _mock.handler);
            EventManager.getInstancet().sendEvent(TEST_LOCAL, TEST_MSG);               
           
        }
       
        [test]
        public function testMoreParam():void
        {
            var TEST_MORE_PARAM:String = 'TEST_MORE_PARAM';
            var TEST_MORE_PARAMS:String = 'TEST_MORE_PARAMS';
           
            _mock.count = 0;
            _mock.handler = function():void
            {
                _mock.count++;
            };
               
            EventManager.getInstancet().addListener(TEST_MORE_PARAM, _mock.handler);
            EventManager.getInstancet().addListener(TEST_MORE_PARAMS, _mock.handler);
           
            EventManager.getInstancet().sendEvent(TEST_MORE_PARAM, TEST_MSG);
            EventManager.getInstancet().sendEvent(TEST_MORE_PARAMS, TEST_MSG, TEST_MSG2);
           
            assertEquals(_mock.count, 2);
        }
       
        [test]
        public function testNullHandler():void
        {
           
            var TEST_FUNCTION_NULL:String = 'TEST_FUNCTION_NULL';
            _mock.count = 0;
            _mock.handler = function(msg:String):void
            {
                if(msg == TEST_MSG)
                    _mock.count++;
            };
           
            EventManager.getInstancet().addListener(TEST_FUNCTION_NULL, _mock.handler);
           
            _mock.handler = null;
           
            EventManager.getInstancet().sendEvent(TEST_FUNCTION_NULL, TEST_MSG);   
           
            assertEquals(_mock.count, 1);
        }

        [test]
        public function testNullObject():void
        {
           
            var TEST_OBJECT_NULL:String = 'TEST_OBJECT_NULL';
            _mock.handler = function(msg:String):void
            {
                _isGetEvent = true;
            };
           
            EventManager.getInstancet().addListener(TEST_OBJECT_NULL, _mock.handler);
           
            _mock = null;
           
            EventManager.getInstancet().sendEvent(TEST_OBJECT_NULL, TEST_MSG);                   
            assertEquals(_isGetEvent, true);
           
        }       
       
        //================
       
        override protected function startUp():void
        {
            _isGetEvent = false;
           
            _mock = new Object();
            _mock.handler = function():void
            {
                _isGetEvent = true;
            };
           
            _mock.handlerParam = function(msg:String):void
            {
                assertEquals(msg, TEST_MSG);
            };
               
            _mock.handlerParams = function(msg:String, msg2:String):void
            {
                var flag:Boolean  = (msg == TEST_MSG);
                var flag2:Boolean = (msg2 == TEST_MSG2);
               
                if(flag && flag2)
                    assertEquals(true, true);
            };
           
            _mock.handlerCurrent = function():void
            {
                _isGetEvent  = true;
            }
           
            _mock.handlerRemoveCurrent = function():void
            {
                _isGetEvent = false;
            }
        }
       
        override protected function shutdown():void
        {
            _mock = null;
            EventManager.getInstancet().clearAll();
        }
    }
}


使用自製的 Unit Test , 執行測試並且顯示的網頁.

[AS] 簡易的事件機制


因為開始製作  "樣板" !

為了區隔 "樣板內" 和 "樣板外" 的事件, 流程

讓兩種事件不能互相溝通,

甚至機制也不一樣來避免被攔截 監聽 等等等

讓 "樣板外" 的部分 不能輕易利用機制外的改變  "樣板內" 流程 !

所以我仿照 pureMVC 的事件機制,

製作了一個簡易的事件類別 !

主要的需求是
1. 可以註冊監聽事件.
2. 可以移除單一事件
3. 可以移除全部的事件
4. 可以傳遞參數
5. 不同於 flash 基本的事件機制.


下面是 這個 事件類別 的 code
package com.tenchiwang.manager
{

    public final class EventManager
    {
        static private var _instance:EventManager;
       
        static public function getInstancet():EventManager
        {
            if(!_instance)
                _instance = new EventManager(new SingletonEnforcer());
            return _instance;
        }
       
        public function EventManager(sc:SingletonEnforcer){}
       
        private var eventList:Object = new Object();
       
        public function addListener(eventType:String, handler:Function):int
        {
            if(eventList[eventType] == undefined)
                eventList[eventType] = new Vector.<Function>();
           
            var vec:Vector.<Function> = eventList[eventType] as Vector.<Function>;
            vec.push(handler);
           
            return vec.length;
        }
       
        public function sendEvent(eventType:String, ...args):void
        {
            if(eventList[eventType] == undefined)
                return;
           
            var vec:Vector.<Function> = eventList[eventType] as Vector.<Function>;
            var len:int = vec.length;
           
            for(var i:int = 0; i < len; i++)
            {
                if(vec[i] != null)
                    vec[i].apply(this, args);
            }
        }
       
        public function removeListener(eventType:String, handler:Function):void
        {
            if(eventList[eventType] == undefined)
                return;
           
            var vec:Vector.<Function> = eventList[eventType] as Vector.<Function>;
            var len:int = vec.length;
           
            for(var i:int = 0; i < len; i++)
            {
                if(vec[i] === handler)
                {
                    vec[i] = null;
                    return;
                }
            }
        }
       
        public function removeListenerAll(eventType:String):void
        {
            if(eventList[eventType] == undefined)
                return;
           
            var vec:Vector.<Function> = eventList[eventType] as Vector.<Function>;
            var len:int = vec.length;
           
            for(var i:int = 0; i < len; i++)
            {
                    vec[i] = null;
            }
           
            vec = null;
           
            delete eventList[eventType];
        }
       
        public function clearAll():void
        {
            for(var key:String in eventList)
                removeListenerAll(key);
        }
       
    }
}
internal class SingletonEnforcer{}



然後為了確認這個 "事件類別" 是可以正常並且如估計運行, 

所以我寫了一些的單元測試!

下一篇, 將介紹我寫了那些單元測試, 來測試這個單元