劉德財(cái) 高建華
(上海師范大學(xué)計(jì)算機(jī)科學(xué)與技術(shù)系 上海 200234)
?
基于函數(shù)式編程語(yǔ)言的事件驅(qū)動(dòng)模型的設(shè)計(jì)與實(shí)現(xiàn)
劉德財(cái)高建華
(上海師范大學(xué)計(jì)算機(jī)科學(xué)與技術(shù)系上海 200234)
函數(shù)式編程語(yǔ)言在移動(dòng)游戲研發(fā)中使用十分普遍,例如客戶端的Lua、JavaScript,服務(wù)端的Ruby、Erlang、JavaScript。但目前對(duì)移動(dòng)游戲開(kāi)發(fā)框架的設(shè)計(jì)大多是基于傳統(tǒng)面向?qū)ο蟮姆椒?,并沒(méi)有很好地利用函數(shù)式編程語(yǔ)言的特性。針對(duì)項(xiàng)目代碼模塊的通信問(wèn)題,通過(guò)借鑒面向?qū)ο?OOP)中的觀察者模式以及現(xiàn)有的函數(shù)式編程語(yǔ)言的開(kāi)源框架Node.js,設(shè)計(jì)并實(shí)現(xiàn)一種適用于函數(shù)式編程語(yǔ)言的事件驅(qū)動(dòng)模型FPEDM(Functional Programming Event Driven Model)。該模型具有簡(jiǎn)單易用,擴(kuò)展性強(qiáng),與移動(dòng)游戲軟件耦合度低、復(fù)用性好的特點(diǎn),應(yīng)用到項(xiàng)目開(kāi)發(fā)中可大大提高開(kāi)發(fā)效率,簡(jiǎn)化項(xiàng)目框架的復(fù)雜度。
函數(shù)式編程事件驅(qū)動(dòng)模型
隨著移動(dòng)智能設(shè)備的配置不斷提高以及移動(dòng)互聯(lián)網(wǎng)技術(shù)的快速發(fā)展,移動(dòng)游戲的開(kāi)發(fā)設(shè)計(jì)變得越來(lái)越重要。移動(dòng)游戲的開(kāi)發(fā)和傳統(tǒng)的端游開(kāi)發(fā)有著很大的區(qū)別,端游一般開(kāi)發(fā)周期長(zhǎng),項(xiàng)目龐大復(fù)雜,引擎技術(shù)由于不開(kāi)源而更新周期較長(zhǎng),開(kāi)發(fā)語(yǔ)言一般為面向?qū)ο笳Z(yǔ)言,如C++、Java等,而移動(dòng)游戲則開(kāi)發(fā)周期短,游戲版本迭代快,引擎技術(shù)開(kāi)源使得引擎技術(shù)更新較快,開(kāi)發(fā)語(yǔ)言趨向于函數(shù)式編程的動(dòng)態(tài)語(yǔ)言,如Lua,JavaScript,Ruby等。目前在移動(dòng)游戲開(kāi)發(fā)框架設(shè)計(jì)領(lǐng)域的研究比較少,實(shí)際開(kāi)發(fā)中很多也還是照搬了傳統(tǒng)面向?qū)ο?OOP)的設(shè)計(jì)思想,沒(méi)有很好地利用函數(shù)式編程語(yǔ)言的特性,使游戲項(xiàng)目變得龐大復(fù)雜,并不能很好地適應(yīng)開(kāi)發(fā)周期短、項(xiàng)目變更快的移動(dòng)開(kāi)發(fā),同時(shí)編程語(yǔ)言本身的差異也使得在實(shí)現(xiàn)OOP設(shè)計(jì)思想上有困難。
本文就移動(dòng)游戲開(kāi)發(fā)框架設(shè)計(jì)中的代碼模塊間通信問(wèn)題進(jìn)行設(shè)計(jì)和研究,通過(guò)借鑒面向?qū)ο?OOP)的觀察者模式和開(kāi)源框架Node.js[1],設(shè)計(jì)并實(shí)現(xiàn)了基于函數(shù)式編程語(yǔ)言的一種事件驅(qū)動(dòng)的模型框架FPEDM,該模型具有易使用,易擴(kuò)展,可復(fù)用的特點(diǎn),同時(shí)模型也充分利用了FP語(yǔ)言的特點(diǎn),更加適合FP語(yǔ)言編寫(xiě)的移動(dòng)游戲項(xiàng)目,更為重要的是把設(shè)計(jì)思想提取成為了一個(gè)文件,不依賴任何具體的移動(dòng)游戲項(xiàng)目。FPEDM應(yīng)用在實(shí)際開(kāi)發(fā)中可大大提升項(xiàng)目開(kāi)發(fā)效率和軟件質(zhì)量。
1.1函數(shù)式編程
函數(shù)式編程和指令式編程(面向過(guò)程編程和面向?qū)ο缶幊?相比較,函數(shù)式編程提升了函數(shù)的地位,函數(shù)是第一類對(duì)象和數(shù)據(jù)的集合,使得編程者對(duì)程序的狀態(tài)和執(zhí)行次序不必太關(guān)心[2]。
1.2函數(shù)式編程語(yǔ)言
函數(shù)式編程語(yǔ)言的構(gòu)成范式的理論基礎(chǔ)是由Alonzo Church等提出并證明與圖靈機(jī)等價(jià)的lambda演算系統(tǒng),在該范式下,程序由函數(shù)和對(duì)函數(shù)的應(yīng)用上下文構(gòu)成[3]。相對(duì)于Objective-C,蘋(píng)果最新發(fā)布的開(kāi)發(fā)語(yǔ)言Swift一個(gè)很重要的改進(jìn)就是就加入了函數(shù)式編程的支持。
1.3事件驅(qū)動(dòng)模型
事件驅(qū)動(dòng)模型在軟件設(shè)計(jì)中主要涵蓋三個(gè)方面:事件源、事件和事件處理者[4]。事件(如GUI中的敲入一個(gè)鍵、單擊鼠標(biāo))發(fā)生后,事件源就會(huì)激發(fā)與此事件相應(yīng)的消息,事件調(diào)度器將事件傳給事件處理者的處理函數(shù)進(jìn)行處理。
2.1FPEDM的設(shè)計(jì)
傳統(tǒng)的軟件工程中,事件驅(qū)動(dòng)模型的設(shè)計(jì)一般如圖1所示。首先由事件注冊(cè)者在事件調(diào)度器中注冊(cè)/移除注冊(cè)特定的事件,事件處理者同時(shí)向調(diào)度器監(jiān)聽(tīng)/取消監(jiān)聽(tīng)特定的事件,事件源發(fā)生事件后告知事件調(diào)度器發(fā)生了某個(gè)事件,事件調(diào)度器收到后通過(guò)派發(fā)告知正在監(jiān)聽(tīng)這個(gè)事件的每一個(gè)處理者,讓所有處理者對(duì)事件作出響應(yīng)[5]。
傳統(tǒng)的事件驅(qū)動(dòng)模型設(shè)計(jì)充分體現(xiàn)了面向?qū)ο蟮脑O(shè)計(jì)思想,優(yōu)點(diǎn)是可擴(kuò)展性好,缺點(diǎn)是設(shè)計(jì)較為復(fù)雜。一個(gè)事件的完成需要多個(gè)參與者,如圖1所示一般需要四個(gè)參與者,這也導(dǎo)致開(kāi)發(fā)人員在理解和使用上成本較高,這在開(kāi)發(fā)周期長(zhǎng)的大型項(xiàng)目中是能夠承受的。但在開(kāi)發(fā)周期較短、迭代迅速的移動(dòng)游戲項(xiàng)目中,顯得有些設(shè)計(jì)過(guò)度,不利于移動(dòng)游戲項(xiàng)目的快速開(kāi)發(fā)和BUG排查。
圖1 面向?qū)ο蟮氖录?qū)動(dòng)模型
本文通過(guò)借鑒面向?qū)ο笾械挠^察者設(shè)計(jì)模式[6]和開(kāi)源框架Node.js[7],設(shè)計(jì)了如圖2所示的基于函數(shù)式編程語(yǔ)言的事件驅(qū)動(dòng)模型(FPEDM)。FPEDM中一個(gè)事件的參與者只剩下事件源和事件處理者,其中事件源通過(guò)引用FPEDM文件,具有了注冊(cè)/取消注冊(cè)事件和事件派發(fā)的功能,事件處理者通過(guò)向事件源(同時(shí)也是事件調(diào)度器)監(jiān)聽(tīng)/取消監(jiān)聽(tīng)特定的事件。FPEDM中的事件源在事件發(fā)生時(shí)派發(fā)事件,所有正在監(jiān)聽(tīng)這一事件的事件處理者都會(huì)收到,從而作出響應(yīng)。FPEDM使得整個(gè)事件驅(qū)動(dòng)的模型變得簡(jiǎn)單易用,模型不依賴于具體的移動(dòng)游戲項(xiàng)目,做到了思想和代碼的100%復(fù)用,并且實(shí)現(xiàn)了和圖1一樣的功能。
圖2 FPEDM
2.2FPEDM的實(shí)現(xiàn)
從圖2中可以看到, FPEDM的關(guān)鍵就是FPEDM文件的實(shí)現(xiàn)。從2.1節(jié)可以知道,事件驅(qū)動(dòng)模型包含6個(gè)過(guò)程:注冊(cè)/取消注冊(cè),監(jiān)聽(tīng)/取消監(jiān)聽(tīng),發(fā)生和派發(fā),其中發(fā)生是事件源的職責(zé),事件源應(yīng)該明確知道事件何時(shí)發(fā)生。因此FPEDM文件只需實(shí)現(xiàn)注冊(cè)/取消注冊(cè),監(jiān)聽(tīng)/取消監(jiān)聽(tīng)、派發(fā)5個(gè)過(guò)程以及引用FPEDM文件的方法即可。
本文中描述FPEDM文件的實(shí)現(xiàn)采用的是moonscript風(fēng)格的偽代碼,moonscript是lua的模板編程語(yǔ)言。
FPEDM文件對(duì)于注冊(cè)/取消注冊(cè)采用了一張哈希表(Hash table)進(jìn)行維護(hù),具體結(jié)構(gòu)如圖3所示,該數(shù)據(jù)結(jié)構(gòu)充分利用了函數(shù)式編程語(yǔ)言的函數(shù)是第一類對(duì)象的特點(diǎn)。哈希表中的關(guān)鍵碼值為事件ID,關(guān)鍵碼值所對(duì)應(yīng)的是所有正在監(jiān)聽(tīng)這一事件的處理函數(shù)的指引(函數(shù)式編程語(yǔ)言中沒(méi)有指針概念,本文稱為指引)的集合。
圖3 FPEDM的數(shù)據(jù)結(jié)構(gòu)
FPEDM文件的添加監(jiān)聽(tīng)方法如圖4所示,其中輸入為事件源,事件ID以及監(jiān)聽(tīng)者(處理函數(shù)),函數(shù)中第一個(gè)參數(shù)self為函數(shù)的調(diào)用者。從圖2可以知道FPEDM文件是被事件源引用的,所以這個(gè)self就是事件源,第3行的findOrCreateListenerTable函數(shù)是創(chuàng)建或者找到已存在的圖3所示的數(shù)據(jù)結(jié)構(gòu),即得到event_id的所有處理函數(shù)的指引集合,第4行為向這個(gè)集合中添加監(jiān)聽(tīng)者listener,最后返回self(事件源)是為了方法的鏈?zhǔn)秸{(diào)用。
圖4FPEDM文件的addListener方法
FPEDM文件的移除監(jiān)聽(tīng)方法如圖5所示,其中輸入為事件源,事件ID以及監(jiān)聽(tīng)者(處理函數(shù)),第4、5行得到event_id的處理函數(shù)集合,并從中移除listener,第6、7行為移除一次性的監(jiān)聽(tīng)者。一次性監(jiān)聽(tīng)者的添加和圖4方法基本一致,只是在event_id后添加once字符組成新event_id以區(qū)別正常監(jiān)聽(tīng)者,它會(huì)在收到事件作出響應(yīng)后自動(dòng)移除監(jiān)聽(tīng)。
圖5FPEDM文件的removeListener方法
FPEDM文件派發(fā)事件的方法如圖6所示,輸入為事件源,事件ID,以及事件派發(fā)時(shí)所帶的參數(shù)(可以多個(gè)參數(shù)),第2-5行得到event_id的處理函數(shù)集合,并依次調(diào)用。每次調(diào)用時(shí)都會(huì)把派發(fā)所帶的參數(shù)傳給處理函數(shù),這樣使得事件源和監(jiān)聽(tīng)者之間可以進(jìn)行數(shù)據(jù)交流,第6-10行為處理一次性監(jiān)聽(tīng)者的響應(yīng),并在10行自動(dòng)移除監(jiān)聽(tīng)。
圖6FPEDM文件的emit方法
引用FPEDM文件的EventEmitter方法如圖7所示,從圖2可知輸入為事件源,第2行對(duì)引用對(duì)象(事件源)設(shè)置標(biāo)志位,標(biāo)記其擁有了FPEDM文件的方法,3-9行依次把FPEDM文件中的方法賦予引用對(duì)象(事件源),并借鑒了Node.js的API風(fēng)格為每個(gè)方法取了別名,便于理解和實(shí)際應(yīng)用。
圖7FPEDM文件的EventEmitter方法
3.1簡(jiǎn)單易用
項(xiàng)目使用FPEDM十分簡(jiǎn)易,它和Web的js文件使用方法類似,只需要在事件源文件中引用FPEDM文件,調(diào)用EventEmitter注冊(cè)自身后即可。圖8展示了FPEDM的使用。
圖8FPEDM的使用示例
3.2充分利用了函數(shù)式編程語(yǔ)言的特點(diǎn)
FPEDM充分利用了FP語(yǔ)言編程的文件模塊化和函數(shù)為第一類對(duì)象的特點(diǎn),無(wú)需再把FP語(yǔ)言偽裝成OOP語(yǔ)言來(lái)使用,靈活度提高了不少,代碼之間的邏輯關(guān)系變得更加清晰,沒(méi)有復(fù)雜的繼承和龐大的UML類圖。
3.3分布式的事件管理機(jī)制
傳統(tǒng)事件調(diào)度器一般都存在輪詢代碼,不斷輪詢每一個(gè)事件的狀態(tài),從而做出是否派發(fā)事件的決定。FPEDM中的事件源通過(guò)引用FPEDM文件后,可以實(shí)現(xiàn)自己管理自己的事件,無(wú)需單獨(dú)的事件調(diào)度器存在。由于這種分布式的事件管理機(jī)制,在一定程度上可以消除移動(dòng)游戲項(xiàng)目中的代碼中心瓶頸問(wèn)題。
3.4實(shí)現(xiàn)了模型和代碼的100%復(fù)用
由于FPEDM的代碼獨(dú)立成了一個(gè)模塊文件,使得只要是和FPEDM文件使用同一門(mén)語(yǔ)言編寫(xiě)的移動(dòng)游戲項(xiàng)目,無(wú)需任何的修改即可直接使用,而且使用過(guò)程十分簡(jiǎn)單方便。同時(shí)理論上任何FP語(yǔ)言都可以實(shí)現(xiàn)FPEDM文件,所以對(duì)使用FP語(yǔ)言編寫(xiě)的項(xiàng)目來(lái)說(shuō),使用FPEDM都是一個(gè)不錯(cuò)的選擇。
3.5保留了OOP式事件驅(qū)動(dòng)模型的優(yōu)勢(shì)
表1列出了FPEDM和傳統(tǒng)面向?qū)ο?OOP)的事件驅(qū)動(dòng)模型對(duì)比結(jié)果,因?yàn)閮蓚€(gè)模型都是對(duì)軟件的一種設(shè)計(jì),所以對(duì)比的指標(biāo)采用了軟件工程中常用的評(píng)價(jià)指標(biāo),包括了復(fù)用性,易用性,可維護(hù)性和擴(kuò)展性[8]。從表1的結(jié)果可以看出FPEDM保留了OOP式事件驅(qū)動(dòng)模型的可維護(hù)性和擴(kuò)展性特點(diǎn),提升了事件驅(qū)動(dòng)模型的代碼復(fù)用性,降低了事件驅(qū)動(dòng)模型的使用和理解門(mén)檻,在移動(dòng)游戲開(kāi)發(fā)中的實(shí)際效果也證實(shí)了這些優(yōu)勢(shì)。
表1 OOP式事件驅(qū)動(dòng)模型和FPEDM對(duì)比
本文通過(guò)借鑒傳統(tǒng)軟件設(shè)計(jì)思想中的觀察者模式和開(kāi)源框架Node.js,設(shè)計(jì)并實(shí)現(xiàn)了基于函數(shù)式編程語(yǔ)言的FPEDM,并把其和傳統(tǒng)OOP式事件驅(qū)動(dòng)模型相比較,得出FPEDM具有簡(jiǎn)單易用、充分利用了FP語(yǔ)言特性、分布式事件管理機(jī)制、代碼復(fù)用率100%和保留了OOP式事件驅(qū)動(dòng)模型優(yōu)勢(shì)的特點(diǎn),使其十分適合用于移動(dòng)游戲項(xiàng)目(FP語(yǔ)言開(kāi)發(fā))的開(kāi)發(fā)和設(shè)計(jì)。日后的研究主要是實(shí)現(xiàn)FPEDM中多事件協(xié)作的處理和事件的異步驅(qū)動(dòng)等。
[1] Node.js的模塊機(jī)制.https://Node.js.org/api/modules.html.
[2] 微軟關(guān)于函數(shù)式編程的定義和解釋.https://msdn.microsoft.com/en-us/library/bb669144.aspx.
[3] 李向陽(yáng),連小綺.函數(shù)式程序范式在語(yǔ)義web中的應(yīng)用[J].中國(guó)科技信息,2006,12(24):79,87.
[4] 韓彪,吳眾欣,欒鐘治,等.一種適于主-從模式網(wǎng)絡(luò)計(jì)算的事件驅(qū)動(dòng)架構(gòu)[J].西安交通大學(xué)學(xué)報(bào),2010,44(2):39-43.
[5] Westermann U,Jain R. Toward a Common Event Model for Multimedia Applications[J]. MultiMedia, IEEE, 2007,14(1):19-29.
[6] Zhu G K. Applying Software Design Patterns in Electromagnetic Field Simulators[J]. Antennas and Propagation Magazine, IEEE, 2012,54(2):174-179.
[7] 王金龍,宋斌,丁銳. Node.js:一種新的Web應(yīng)用構(gòu)建技術(shù)[J].現(xiàn)代電子技術(shù),2015,38(6):70-73.
[8] Ian Sommerville. 軟件工程[M]. 程成,陳霞,等譯.北京:機(jī)械工業(yè)出版社,2013: 178-192.
DESIGN AND IMPLEMENTATION OF EVENT-DRIVEN MODEL BASED ON FUNCTIONAL PROGRAMMING LANGUAGE
Liu DecaiGao Jianhua
(Department of Computer Science and Technology, Shanghai Normal University, Shanghai 200234,China)
Functional programming language (FPL) has been widely used in the development of mobile games, such as Lua and JavaScript on client, Ruby, Erlang and JavaScript on server. However, most of the designs in regard to mobile game development framework are based on traditional object-oriented method, they do not make good use of the characteristics of FPL. In this paper, focuses on the communication in modules of project code, we designed and implemented an event-driven model applicable to FPL, named FPEDM, by referring the observer pattern of object-oriented programming (OOP) and existing open source framework of FPL Node.js. The model is simple and easy to use, has strong scalability, low coupling dependency with specific mobile game projects, and good reusability. By applying it to project development, the development efficiency can be greatly improved, the complexity of the project framework can be simplified as well.
Functional programmingEvent-driven model
2015-06-06。國(guó)家自然科學(xué)基金項(xiàng)目(61073163);上海市企業(yè)自主創(chuàng)新專項(xiàng)資金項(xiàng)目(滬CXY-2013-88)。劉德財(cái),碩士生,主研領(lǐng)域:軟件可靠性設(shè)計(jì)理論與方法。高建華,教授。
TP3
A
10.3969/j.issn.1000-386x.2016.09.002