教你一招:嵌入式編程技法之"數(shù)據(jù)驅(qū)動編程"
1、聊一聊
今天為大家介紹數(shù)據(jù)驅(qū)動編程技巧,能夠幫助大家在平時寫出高質(zhì)量的代碼。
2、什么是數(shù)據(jù)驅(qū)動編程
作者一直覺得"沒有設(shè)計思想的代碼是沒有靈魂的",所以在這么多年的程序設(shè)計中一般首先設(shè)計程序架構(gòu)然后再進(jìn)行編碼,之前作者為大家介紹了一下<【殺手锏】用“萬能C編程”來引出"面向?qū)ο?>,應(yīng)該有很多之前沒有接觸過這方面內(nèi)容的小伙伴從中受益,同時也希望作者能夠帶來更多類似的文章,所以就有了該新作。
1
這里拿MP3播放器作為一個例子為大家引出數(shù)據(jù)驅(qū)動思想,使用MP3播放器進(jìn)行音樂播放需要有MP3格式的文件,并且不同的歌曲都會遵循該格式的文件依次存放。
那么對應(yīng)的MP3播放器會按照MP3音樂文件進(jìn)行對應(yīng)的解析,這樣對于不同的音樂文件都是統(tǒng)一的解析方式,并不需要換另外一種解析方式,以后我們想播放其他的音樂僅僅只需要添加對應(yīng)的音樂文件即可,而并不需要再更新對應(yīng)的音樂播放器。
同時當(dāng)我們需要切歌、定義播放模式等等,僅僅只需要對播放文件索引進(jìn)行邏輯處理即可,該實例就為我們展示了數(shù)據(jù)驅(qū)動思想的特點: 認(rèn)為數(shù)據(jù)易變,而邏輯相對比較固定,這樣便把數(shù)據(jù)和邏輯分離,降低代碼邏輯的重復(fù),增強(qiáng)代碼的聚合度。
2
在之前的文章中作者在介紹結(jié)構(gòu)體的時候說到了一個妙處不知道大家還是否記得?<【典藏】大佬們都在用的結(jié)構(gòu)體進(jìn)階小技巧>,在該文中說到結(jié)構(gòu)體可以囊括所有數(shù)據(jù),并且幾乎所有的事物進(jìn)行抽象以后都可以歸為數(shù)據(jù),所以采用數(shù)據(jù)驅(qū)動開發(fā)也將是行之有效的思路。
分析一下:
左邊是所有的數(shù)據(jù),包括用戶的輸入,所采集的數(shù)據(jù)、還有各種處理數(shù)據(jù)的方法都是數(shù)據(jù),獲得所有的數(shù)據(jù)以后,正常思路是直接編碼進(jìn)行數(shù)據(jù)解析,最終實現(xiàn)項目需求。
然而對于數(shù)據(jù)驅(qū)動開發(fā),并不是立馬進(jìn)行數(shù)據(jù)解析,而是首先進(jìn)行數(shù)據(jù)庫設(shè)計,這里作者所謂的數(shù)據(jù)庫并不是mysql什么的,而是一種數(shù)據(jù)結(jié)構(gòu),表明數(shù)據(jù)與數(shù)據(jù)的關(guān)系等,類似于前面提到的MP3格式文件。
設(shè)計好數(shù)據(jù)庫以后,需要構(gòu)造數(shù)據(jù)庫解析的方式,該解析方式的目標(biāo)是實現(xiàn)項目功能目標(biāo)的全部或者是一部分,也就類似于前面提到的MP3播放器。
3、實現(xiàn)合集
對于數(shù)據(jù)驅(qū)動編程最大的難點在抽象出數(shù)據(jù)庫和數(shù)據(jù)解析(有點類似于一個文本解析器),這兩部分的設(shè)計的靈活度就決定了程序的擴(kuò)展性和緊湊性。
目前比較簡單的就是用數(shù)組等作為數(shù)據(jù)庫,數(shù)據(jù)解析基本上是查表,所以也有很多小伙伴直接稱為表驅(qū)動編程,然而對于數(shù)據(jù)驅(qū)動編程遠(yuǎn)遠(yuǎn)可以設(shè)計得比表驅(qū)動復(fù)雜,所以這也是在開發(fā)中選擇數(shù)據(jù)驅(qū)動方法編碼的一個決定因素。
下面作者還是以比較基礎(chǔ)的實例跟大家分析分析:
1
參考demo:
1#include <stdio.h>
2#include <stdlib.h>
3
4//構(gòu)造數(shù)據(jù)
5#define STATUE1 (0)
6#define STATUE2 (1)
7#define STATUE3 (2)
8#define MAX_STATUE (3)
9
10int Statue1Process(int param);
11int Statue2Process(int param);
12int Statue3Process(int param);
13/***************************************
14 * Fuciton:數(shù)據(jù)驅(qū)動編程實例
15 * Author :(公眾號:最后一個bug)
16 ***************************************/
17 //構(gòu)造數(shù)據(jù)庫
18typedef int (*PStatueMachineProcess)(int param);
19PStatueMachineProcess MachineProcess[MAX_STATUE] =
20{
21 Statue1Process,
22 Statue2Process,
23 Statue3Process
24};
25
26//構(gòu)造數(shù)據(jù)
27int Statue1Process(int param)
28{
29 printf("Statue1Process\n");
30 return STATUE2;
31}
32
33int Statue2Process(int param)
34{
35 printf("Statue2Process\n");
36 return STATUE3;
37}
38
39int Statue3Process(int param)
40{
41 printf("Statue3Process\n");
42 return STATUE1;
43}
44
45
46int main(int argc, char *argv[]) {
47
48 int statue = STATUE1;
49 int count = 0;
50
51 //數(shù)據(jù)庫解析和邏輯引導(dǎo)
52 while(1)
53 {
54 statue = (MachineProcess[statue])(0);
55 count++;
56 if(count > 10)break;
57 }
58
59 printf("公眾號:最后一個bug\n");
60
61 return 0;
62}
分析一下:
這樣實現(xiàn)狀態(tài)機(jī)應(yīng)該比if-else方便多了,上面的代碼對于有一定編程經(jīng)驗的小伙伴而言,似乎僅僅只是用了一個函數(shù)指針數(shù)據(jù)就搞定了,如果我們再認(rèn)真解讀一下,其實在該過程中僅僅就是狀態(tài)數(shù)據(jù)發(fā)生了變化,其他均是綁定一套連貫處理,這里的狀態(tài)數(shù)據(jù)作為了數(shù)組的標(biāo)識,所以在此也說明數(shù)據(jù)驅(qū)動的特點:僅僅只對數(shù)據(jù)敏感。
從上面的處理可以發(fā)現(xiàn)該方式可以減少switch和if-else的使用。
2
參考demo:
1typedef enum
2{
3 OBJ_TYPE_1,
4 OBJ_TYPE_2,
5 OBJ_TYPE_3,
6}ENUM_OBJ_TYPE;
7
8typedef enum
9{
10 SIG_TYPE_1,
11 SIG_TYPE_2,
12 SIG_TYPE_3,
13}ENUM_SIG_TYPE;
14
15typedef int (*MessageProcess)(int param);
16
17typedef struct _tag_MessagePack
18{
19 ENUM_OBJ_TYPE ObjType; //對象類型
20 ENUM_SIG_TYPE SignalType; //消息類型
21 MessageProcess MessageProc; //消息處理
22}sMessagePack ;
23
24int obj1_Sig1_proc(int param)
25{
26
27}
28.....
29 /***************************************
30 * Fuciton: 構(gòu)造數(shù)據(jù)庫
31 * Author :(公眾號:最后一個bug)
32 ***************************************/
33sMessagePack stMessagePack[]=
34{
35 {OBJ_TYPE_1, SIG_TYPE_1, obj1_Sig1_proc}
36 {OBJ_TYPE_1, SIG_TYPE_2, obj1_Sig2_proc}
37 {OBJ_TYPE_2, SIG_TYPE_1, obj2_Sig1_proc}
38 {OBJ_TYPE_2, SIG_TYPE_2, obj2_Sig2_proc}
39 {OBJ_TYPE_3, SIG_TYPE_1, obj3_Sig1_proc}
40 {OBJ_TYPE_3, SIG_TYPE_2, obj3_Sig2_proc}
41};
42
43/***************************************
44 * Fuciton:消息循環(huán)處理
45 * Author :(公眾號:最后一個bug)
46 ***************************************/
47MessageProcess MessageLoop(ENUM_OBJ_TYPE objType, ENUM_SIG_TYPE signalType)
48{
49 int i = 0;
50 for (i = 0; i < (sizeof(stMessagePack)/sizeof(sMessagePack)); i ++)
51 {
52 if ((stMessagePack[i].ObjType == objType) && (stMessagePack[i].SignalType == signalType))
53 {
54 return stMessagePack[i].MessageProc;
55 }
56 }
57 return NULL;
58}
分析一下:
上面代碼僅僅只是給大家簡單的體驗一下數(shù)據(jù)驅(qū)動編程方法,對于效率等等作者沒有過多的考慮,其性能與所設(shè)計的數(shù)據(jù)結(jié)構(gòu)有關(guān)系。所以這樣的處理也避免了很多小伙伴進(jìn)行硬編碼的習(xí)慣。
4、最后小結(jié)
最后對于數(shù)據(jù)驅(qū)動編程并不是萬能的,可能對于有些設(shè)計反而起到副作用,一般用于實現(xiàn)邏輯相對比較清晰的處理,不過對于簡化條件語句還是非常好用的,所以對于這些編程思路,需要根據(jù)具體情況進(jìn)行選擇,同時也要對目標(biāo)項目有較強(qiáng)的理解能力。
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!