找回密码
 注册
关于网站域名变更的通知
查看: 306|回复: 1
打印 上一主题 下一主题

什么是数据驱动编程

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2020-1-14 11:11 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
+ H/ T% l! u6 I5 L
前言:3 C' c# b9 E6 ?  D. x

8 T; v1 ?. |: ^  e) V% H9 }8 j, E; @2 \最近在学习《Unix编程艺术》。以前粗略的翻过,以为是介绍unix工具的。现在认真的看了下,原来是介绍设计原则的。它的核心就是第一章介绍的unix的哲学以及17个设计原则,而后面的内容就是围绕它来展开的。以前说过,要学习适合自己的资料,而判断是否适合的一个方法就是看你是否能够读得下去。我对这本书有一种相见恨晚的感觉。推荐有4~6年工作经验的朋友可以读一下。
* [; j; ?& C4 L% r! R5 L7 u
9 g% r' F- W& q3 L3 @7 n5 n正题:2 G3 w* b# i4 u' D5 c

8 v3 h& H# J' J作者在介绍Unix设计原则时,其中有一条为“表示原则:把知识叠入数据以求逻辑质朴而健壮”。结合之前自己的一些经验,我对这个原则很有共鸣,所以先学习了数据驱动编程相关的内容,这里和大家分享出来和大家一起讨论。
$ C' w5 m$ {2 [( W! x! x1 w( i: O- m7 q* A/ }+ n6 ~6 {
数据驱动编程的核心
6 i) |# B/ T5 [. b, ]
  U* Q( H" Y+ u' @, `" B' l数据驱动编程的核心出发点是相对于程序逻辑,人类更擅长于处理数据。数据比程序逻辑更容易驾驭,所以我们应该尽可能的将设计的复杂度从程序代码转移至数据。5 j9 Y. u3 H, A% ?8 e, c8 M9 |

0 J0 V1 q  X3 A' M7 h1 ?. a7 L真的是这样吗?让我们来看一个示例。4 |& p% u# d( ], U$ w, e, Y
, g: Z3 z. Z2 o
假设有一个程序,需要处理其他程序发送的消息,消息类型是字符串,每个消息都需要一个函数进行处理。第一印象,我们可能会这样处理:
% [" q/ g6 }* m8 ]) Fvoid msg_proc(const char *msg_type, const char *msg_buf)
5 T% N8 x1 M) a! I. F{ 2 q) [) O3 Y% X8 b/ {! l
    if (0 == strcmp(msg_type, "inivite"))
, u" n+ ~, U$ R$ y- Q3 `    { ! l' E/ F( ?8 ]; B
        inivite_fun(msg_buf); 1 I( N# }6 A% b% o
    }
! B& ]; f5 k1 _$ \% a) Y    else if (0 == strcmp(msg_type, "tring_100")) / }- k0 c. s: [& v
    {
$ T1 L9 Y- ]# d) I2 B+ r# `        tring_fun(msg_buf);
9 ^2 O) D' e& `3 A  h    } 3 a$ t+ [9 O6 E8 I7 b4 i0 w
    else if (0 == strcmp(msg_type, "ring_180"))
) c" k5 o  s5 G+ Q  O    { 6 `+ H( w' S, i
        ring_180_fun(msg_buf); 8 E! _2 r1 {8 l% w8 e$ Y: {
    }
$ U0 G) \, K% W3 T! C+ {, T/ L# J    else if (0 == strcmp(msg_type, "ring_181")) 9 |# n8 x' p' @
    {
' H' o( ?1 X" r        ring_181_fun(msg_buf); ) H8 H* ~' X; J' }6 I. v8 D+ g
    }
; @9 H7 h1 p" H& j- @7 y8 f    else if (0 == strcmp(msg_type, "ring_182"))
2 n5 s* S" X8 `    {
+ Q  z0 S! E! r        ring_182_fun(msg_buf);
8 j( I0 x1 C. I' m    } ' P6 j; q" J1 C. W
    else if (0 == strcmp(msg_type, "ring_183"))
! a) U  ]" P& ~    {
# c# V' R: X2 f6 L: d5 N        ring_183_fun(msg_buf); 4 R6 G1 ^: J. w( ^: f
    }
9 ~" _- T" D/ z- D    else if (0 == strcmp(msg_type, "ok_200"))
, F2 p- U" f6 a( B$ K, ~5 f% I) @' ~; ~    { 9 \- D9 G( u& ^- e6 W
        ok_200_fun(msg_buf); ! [" _1 Y/ Z- \0 `
    }4 j1 C. B9 }4 |9 B1 ~2 F
# b$ q: Q2 U( ]  R
    。。。。。。 8 r9 y& V% a; L
    else if (0 == strcmp(msg_type, "fail_486"))
! v/ y  \* J( L% ]# \. G$ y    { 2 p+ u( n6 p3 G
        fail_486_fun(msg_buf); 4 j5 d0 _7 K) b  W/ H/ V( c  a& C
    } 3 e! E' n, v. `
    else 9 p9 D9 Q& g6 V5 H# |
    { 5 ~" G: Y8 [5 Y  R- l
        log("未识别的消息类型%s\n", msg_type); + C7 F6 P' ?# a- u  z" f* J
    }
, e" ]5 i! D/ l5 ?  Q8 B) d  J} % ^0 D' J3 ~+ J. W9 H9 [2 ^
上面的消息类型取自sip协议(不完全相同,sip协议借鉴了http协议),消息类型可能还会增加。看着常常的流程可能有点累,检测一下中间某个消息有没有处理也比较费劲,而且,没增加一个消息,就要增加一个流程分支。8 y1 V$ X+ l( x9 }3 ]
& }, l7 y1 |2 g" k: F4 a5 l
按照数据驱动编程的思路,可能会这样设计: " v' [# J; c6 e. ?+ U" G+ i
typedef void (*SIP_MSG_FUN)(const char *);
( Y. ~2 u% g! u; ]
" i# d( _: Q& `+ b! x* b7 K: `- t$ Atypedef struct __msg_fun_st / H( S8 H8 p3 S  a. x
{ 1 U+ E  r$ D/ l" ]4 [0 x$ w
    const char *msg_type;//消息类型 ' y+ Y  T" d, h* }( D* \! ]
    SIP_MSG_FUN fun_ptr;//函数指针 * D" B' I2 Y8 }" b( c( M
}msg_fun_st;5 s6 P/ z9 i8 W) w" J
* o3 h9 [+ P/ R; j+ R
msg_fun_st msg_flow[] = , f  I* j& g4 A6 b8 I- O( c- Q
{
) S2 S; ]6 n6 O& e4 j$ S# o        {"inivite", inivite_fun}, ; C' t: g! r5 ~6 z* }
        {"tring_100", tring_fun}, 4 C- j' _& c. ^& J, \
        {"ring_180", ring_180_fun}, / [, c- K# e6 X2 C( F, e6 j
        {"ring_181", ring_181_fun}, / T- }- X$ C9 g4 n
        {"ring_182", ring_182_fun}, ) t0 ^( |! B' I* z! K9 I
        {"ring_183", ring_183_fun},
5 |) F2 I5 }- F1 Z6 _0 _) d. w/ g        {"ok_200", ok_200_fun},
) \# W4 G0 d9 K% k4 F& M
4 k+ b9 o/ J& V: J: d+ H        。。。。。。 + r' y; M; w# d& D8 s! t. ]
        {"fail_486", fail_486_fun}
/ K! K9 V3 t  ^4 L};
2 m2 E/ m+ b7 S7 X  W  b' N- s9 }/ c9 e6 w: [6 r2 j
void msg_proc(const char *msg_type, const char *msg_buf) * \4 J: `. e6 V0 b& S; e
{ ! Z0 V4 @# F1 l
    int type_num = sizeof(msg_flow) / sizeof(msg_fun_st);   g) a* U: ~! n0 l2 I$ Q+ n+ }8 ^
    int i = 0;2 J+ m2 {# E$ Q8 G

7 y1 E) ?/ A1 C; |/ I' s. i    for (i = 0; i < type_num; i++)
- t9 x% T: _4 a( a+ {: L    {   U' q$ p0 P: g' z' y: k# ~. u( F. Q8 i
        if (0 == strcmp(msg_flow.msg_type, msg_type)) & K* V' L# X! z9 _
        { # B! x; P% L( A! N' m
            msg_flow.fun_ptr(msg_buf); 1 w9 ?2 f& F; W  J" |5 N2 g, r
            return ; % [5 l( T4 @5 }! P
        } - I0 ]) G' \3 F0 u" B- C: q0 J' V
    } 6 {: G3 z" j- j# N# T
    log("未识别的消息类型%s\n", msg_type); , k( X, U8 L! C2 ?) k% K
}
2 ?; s  j) G6 g4 ~# {5 e4 G9 E: N" j# y
下面这种思路的优势:" @% ]7 X+ D- M0 l
  ~" `. P4 x% ^1 S) m  X
1、可读性更强,消息处理流程一目了然。
% j1 F/ ^# o7 @3 t
, n1 F0 t) l$ i" y2、更容易修改,要增加新的消息,只要修改数据即可,不需要修改流程。- Q3 t9 ^9 v: L6 _. I9 O/ b
% B* J7 p3 x8 ?4 G; K" F
3、重用,第一种方案的很多的else if其实只是消息类型和处理函数不同,但是逻辑是一样的。下面的这种方案就是将这种相同的逻辑提取出来,而把容易发生变化的部分提到外面。( |8 z* P" p/ v( m* d9 g
1 @& }6 H( s; |1 @
隐含在背后的思想:1 {: v/ y/ r8 m8 d& Z6 l
$ q. ^9 _! y* @# v9 ~
很多设计思路背后的原理其实都是相通的,隐含在数据驱动编程背后的实现思想包括:
& N) o  F0 r9 U* B  ^& U& b
+ y7 |' B3 A, z$ r- [; Z# o) I1、控制复杂度。通过把程序逻辑的复杂度转移到人类更容易处理的数据中来,从而达到控制复杂度的目标。
/ i( [' n. T5 f" C( r9 v; d- H$ x4 d1 \/ K
2、隔离变化。像上面的例子,每个消息处理的逻辑是不变的,但是消息可能是变化的,那就把容易变化的消息和不容易变化的逻辑分离。: S/ O* Q! z! ~  Q0 F
4 r  V' X' t9 z4 q
3、机制和策略的分离。和第二点很像,本书中很多地方提到了机制和策略。上例中,我的理解,机制就是消息的处理逻辑,策略就是不同的消息处理(后面想专门写一篇文章介绍下机制和策略)。
' C. l6 T+ j1 N% |9 Q2 ~; `' Z4 M! \
数据驱动编程可以用来做什么:
+ |5 n8 W' W% R  u
- r% A( w3 E  A: a如上例所示,它可以应用在函数级的设计中。
0 A8 o0 e2 V! E6 x8 M! N6 A: q+ V1 i! q
同时,它也可以应用在程序级的设计中,典型的比如用表驱动法实现一个状态机(后面写篇文章专门介绍)。
0 x0 D# o$ g  a, @
+ N1 _/ u5 x: a4 u" {也可以用在系统级的设计中,比如DSL(这方面我经验有些欠缺,目前不是非常确定)。
/ N- p5 ^% q7 T3 b+ ^; ]$ H" F7 y* t7 X: q& o3 }$ n
它不是什么:
. _0 e' a: R1 y: Q/ ]6 O
, @; ^" o  r( y6 ^1、 它不是一个全新的编程模型:它只是一种设计思路,而且历史悠久,在unix/linux社区应用很多;$ K& [. B: H& k9 l( |0 }

* x! c/ |3 \- L2、它不同于面向对象设计中的数据:“数据驱动编程中,数据不但表示了某个对象的状态,实际上还定义了程序的流程;OO看重的是封装,而数据驱动编程看重的是编写尽可能少的代码。”, h- n6 f0 g- M# B" L8 E- v
! Q4 ]" u% J, Y8 {8 n
书中的值得思考的话:
3 r) P  ]: C* y% c' R3 M  F7 G3 B: c3 p' Z) n0 T4 \0 N- ^
数据压倒一切。如果选择了正确的数据结构并把一切组织的井井有条,正确的算法就不言自明。编程的核心是数据结构,而不是算法。——Rob Pike  M  J, w" q# r9 }* K) F
. y" t% p" H, ^) T4 H7 `0 v
程序员束手无策。。。。。只有跳脱代码,直起腰,仔细思考数据才是最好的行动。表达式编程的精髓。——Fred Brooks
) t3 ]) O- e! ?: @  J% d
4 T" H7 i2 A% y; w" [' D数据比程序逻辑更易驾驭。尽可能把设计的复杂度从代码转移至数据是个好实践。——《unix编程艺术》作者。

该用户从未签到

2#
发表于 2020-1-14 20:01 | 只看该作者
数据驱动编程
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-11-26 02:44 , Processed in 0.156250 second(s), 23 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表