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
終於有花一小段時間 弄了一個簡單的版本!
雖然是使用別人的類別作的
不過大致的瞭解了其中的技術是怎樣做的
使用了一些 視差效果 的套件
幾乎每一款都有各自的特點!
使用方法也都不同
但是我最後決定使用的是 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
下面是我的測試圖片!
我參考的幾篇文章
http://anycent.blog.163.com/blog/static/84498427201022335056341/
https://ant.apache.org/manual/Tasks/exec.html
http://forums.adobe.com/message/3385890#3385890#3385890
其實還有一個需求是可以整合 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 介面都可以使用.
綜合以上 有了下面的類別
所以我仿照 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{} 然後為了確認這個 "事件類別" 是可以正常並且如估計運行,
所以我寫了一些的單元測試!
下一篇, 將介紹我寫了那些單元測試, 來測試這個單元
訂閱:
意見
(
Atom
)
