本文示例代碼采用的是c語(yǔ)言丧慈。
之前介紹過(guò)數(shù)據(jù)驅(qū)動(dòng)編程《什么是數(shù)據(jù)驅(qū)動(dòng)編程》析命。里面介紹了一個(gè)簡(jiǎn)單的數(shù)據(jù)驅(qū)動(dòng)手法。今天更進(jìn)一步,介紹一個(gè)稍微復(fù)雜鹃愤,更加實(shí)用的一點(diǎn)手法——表驅(qū)動(dòng)法簇搅。
關(guān)于表驅(qū)動(dòng)法,在《unix編程藝術(shù)》中有提到软吐,更詳細(xì)的描述可以看一下《代碼大全》馍资,有一章專門進(jìn)行描述(大概是第八章)。
簡(jiǎn)單的表驅(qū)動(dòng):
《什么是數(shù)據(jù)驅(qū)動(dòng)編程》中有一個(gè)代碼示例关噪。它其實(shí)也可以看做是一種表驅(qū)動(dòng)手法鸟蟹,只不過(guò)這個(gè)表相對(duì)比較簡(jiǎn)單,它在收到消息后使兔,根據(jù)消息類型確定使用調(diào)用什么函數(shù)進(jìn)行處理建钥。
復(fù)雜一點(diǎn)的表驅(qū)動(dòng):
考慮一個(gè)消息(事件)驅(qū)動(dòng)的系統(tǒng),系統(tǒng)的某一模塊需要和其他的幾個(gè)模塊進(jìn)行通信虐沥。它收到消息后熊经,需要根據(jù)消息的發(fā)送方,消息的類型欲险,自身的狀態(tài)镐依,進(jìn)行不同的處理。比較常見(jiàn)的一個(gè)做法是用三個(gè)級(jí)聯(lián)的switch分支實(shí)現(xiàn)通過(guò)硬編碼來(lái)實(shí)現(xiàn):
[cpp]?view plain?copy
switch(sendMode)??
{??
case:??
}??
switch(msgEvent)??
{??
case:??
}??
switch(myStatus)??
{??
case:??
}??
這種方法的缺點(diǎn):
1天试、可讀性不高:找一個(gè)消息的處理部分代碼需要跳轉(zhuǎn)多層代碼槐壳。
2、過(guò)多的switch分支喜每,這其實(shí)也是一種重復(fù)代碼务唐。他們都有共同的特性,還可以再進(jìn)一步進(jìn)行提煉带兜。
3枫笛、可擴(kuò)展性差:如果為程序增加一種新的模塊的狀態(tài),這可能要改變所有的消息處理的函數(shù)刚照,非常的不方便刑巧,而且過(guò)程容易出錯(cuò)。
4无畔、程序缺少主心骨:缺少一個(gè)能夠提綱挈領(lǐng)的主干啊楚,程序的主干被淹沒(méi)在大量的代碼邏輯之中。
用表驅(qū)動(dòng)法來(lái)實(shí)現(xiàn):
根據(jù)定義的三個(gè)枚舉:模塊類型檩互,消息類型特幔,自身模塊狀態(tài),定義一個(gè)函數(shù)跳轉(zhuǎn)表:
[cpp]?view plain?copy
typedef?struct??__EVENT_DRIVE??
{??
MODE_TYPE?mod;//消息的發(fā)送模塊??
EVENT_TYPE?event;//消息類型??
STATUS_TYPE?status;//自身狀態(tài)??
EVENT_FUN?eventfun;//此狀態(tài)下的處理函數(shù)指針??
}EVENT_DRIVE;??
EVENT_DRIVE?eventdriver[]?=//這就是一張表的定義闸昨,不一定是數(shù)據(jù)庫(kù)中的表蚯斯。也可以使自己定義的一個(gè)結(jié)構(gòu)體數(shù)組薄风。??
{??
????{MODE_A,?EVENT_a,?STATUS_1,?fun1}??
????{MODE_A,?EVENT_a,?STATUS_2,?fun2}??
????{MODE_A,?EVENT_a,?STATUS_3,?fun3}??
????{MODE_A,?EVENT_b,?STATUS_1,?fun4}??
????{MODE_A,?EVENT_b,?STATUS_2,?fun5}??
????{MODE_B,?EVENT_a,?STATUS_1,?fun6}??
????{MODE_B,?EVENT_a,?STATUS_2,?fun7}??
????{MODE_B,?EVENT_a,?STATUS_3,?fun8}??
????{MODE_B,?EVENT_b,?STATUS_1,?fun9}??
????{MODE_B,?EVENT_b,?STATUS_2,?fun10}??
};??
int?driversize?=?sizeof(eventdriver)?/?sizeof(EVENT_DRIVE)//驅(qū)動(dòng)表的大小??
EVENT_FUN?GetFunFromDriver(MODE_TYPE?mod,?EVENT_TYPE?event,?STATUS_TYPE?status)//驅(qū)動(dòng)表查找函數(shù)??
{??
int?i?=?0;??
for?(i?=?0;?i?<?driversize;?i?++)??
????{??
if?((eventdriver[i].mod?==?mod)?&&?(eventdriver[i].event?==?event)?&&?(eventdriver[i].status?==?status))??
????????{??
return?eventdriver[i].eventfun;??
????????}??
????}??
return?NULL;??
}??
這種方法的好處:
1、提高了程序的可讀性拍嵌。一個(gè)消息如何處理遭赂,只要看一下驅(qū)動(dòng)表就知道,非常明顯横辆。
2撇他、減少了重復(fù)代碼。這種方法的代碼量肯定比第一種少狈蚤。為什么困肩?因?yàn)樗岩恍┲貜?fù)的東西:switch分支處理進(jìn)行了抽象,把其中公共的東西——根據(jù)三個(gè)元素查找處理方法抽象成了一個(gè)函數(shù)GetFunFromDriver外加一個(gè)驅(qū)動(dòng)表脆侮。
3锌畸、可擴(kuò)展性。注意這個(gè)函數(shù)指針靖避,他的定義其實(shí)就是一種契約潭枣,類似于java中的接口,c++中的純虛函數(shù)幻捏,只有滿足這個(gè)條件(入?yún)⑴枥纾祷刂担趴梢宰鳛橐粋€(gè)事件的處理函數(shù)篡九。這個(gè)有一點(diǎn)插件結(jié)構(gòu)的味道谐岁,你可以對(duì)這些插件進(jìn)行方便替換,新增瓮下,刪除翰铡,從而改變程序的行為。而這種改變讽坏,對(duì)事件處理函數(shù)的查找又是隔離的(也可以叫做隔離了變化)。例证、
4路呜、程序有一個(gè)明顯的主干。
5织咧、降低了復(fù)雜度胀葱。通過(guò)把程序邏輯的復(fù)雜度轉(zhuǎn)移到人類更容易處理的數(shù)據(jù)中來(lái),從而達(dá)到控制復(fù)雜度的目標(biāo)笙蒙。
繼承與組合
考慮一個(gè)事件驅(qū)動(dòng)的模塊抵屿,這個(gè)模塊管理很多個(gè)用戶,每個(gè)用戶需要處理很多的事件捅位。那么轧葛,我們建立的驅(qū)動(dòng)表就不是針對(duì)模塊了搂抒,而是針對(duì)用戶,應(yīng)該是用戶在某狀態(tài)下尿扯,收到某模塊的某事件的處理求晶。我們?cè)偌僭O(shè)用戶可以分為不同的級(jí)別,每個(gè)級(jí)別對(duì)上面的提到的處理又不盡相同衷笋。
用面向?qū)ο蟮乃悸贩夹樱覀兛梢钥紤]設(shè)計(jì)一個(gè)用戶的基類,實(shí)現(xiàn)相同事件的處理方法辟宗;根據(jù)級(jí)別不同爵赵,定義幾個(gè)不同的子類,繼承公共的處理泊脐,再分別實(shí)現(xiàn)不同的處理空幻。這是最常見(jiàn)的一種思路,可以叫它繼承法晨抡。
如果用表驅(qū)動(dòng)法怎么實(shí)現(xiàn)氛悬?直接設(shè)計(jì)一個(gè)用戶的類,沒(méi)有子類耘柱,也沒(méi)有具體的事件的處理方法如捅。它有一個(gè)成員,就是一個(gè)驅(qū)動(dòng)表调煎,它收到事件后镜遣,全部委托給這個(gè)驅(qū)動(dòng)表去進(jìn)行處理。針對(duì)用戶的級(jí)別不同士袄,可以定義多個(gè)不同的驅(qū)動(dòng)表來(lái)裝配不同的對(duì)象實(shí)例悲关。這個(gè)可以叫他組合法。
繼承和組合在《設(shè)計(jì)模式》也有提到娄柳。組合的優(yōu)勢(shì)在于它的可擴(kuò)展性寓辱,彈性,強(qiáng)調(diào)封裝性赤拒。(繼承和組合可以參考這篇文章:面向?qū)ο笾^承組合淺談)
至于這種情況下的驅(qū)動(dòng)表秫筏,可以繼續(xù)使用結(jié)構(gòu)體,也可以使用對(duì)象挎挖。
上面的方法的一點(diǎn)性能優(yōu)化建議:
如果對(duì)性能要求不高这敬,上面的方法足可以應(yīng)付。如果性能要求很高蕉朵,可以進(jìn)行適當(dāng)?shù)膬?yōu)化崔涂。比如,可以建立一個(gè)多維數(shù)組始衅,每一維分別表示模塊冷蚂,狀態(tài)缭保,消息。這樣帝雇,就可以根據(jù)這三者的枚舉直接根據(jù)下標(biāo)定位到處理函數(shù)涮俄,而不是查表。(其實(shí)還是數(shù)據(jù)驅(qū)動(dòng)的思想:數(shù)據(jù)結(jié)構(gòu)是靜態(tài)的算法尸闸。)
數(shù)據(jù)驅(qū)動(dòng)編程再更高級(jí)彻亲,更為抽象一點(diǎn)的,應(yīng)該就是流程腳本或者DSL了吮廉。我曾經(jīng)寫過(guò)一個(gè)簡(jiǎn)單的寄生在xml上的腳本來(lái)描述流程苞尝。這一塊后面抽時(shí)間介紹。
參考鏈接:https://blog.csdn.net/chgaowei/article/details/6966857