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

什么是数据驱动编程

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x

( p& b. e0 P. A前言:4 h( w. b3 X+ l" }

. }6 u7 C- C% |# H- L  r最近在学习《Unix编程艺术》。以前粗略的翻过,以为是介绍unix工具的。现在认真的看了下,原来是介绍设计原则的。它的核心就是第一章介绍的unix的哲学以及17个设计原则,而后面的内容就是围绕它来展开的。以前说过,要学习适合自己的资料,而判断是否适合的一个方法就是看你是否能够读得下去。我对这本书有一种相见恨晚的感觉。推荐有4~6年工作经验的朋友可以读一下。
0 V! P8 T: b2 `1 @7 Y' p/ o6 q+ J$ l5 Y% A4 T+ R
正题:
- C2 l; I9 @' ]9 r4 f; Y
* u* ^* C& p1 S作者在介绍Unix设计原则时,其中有一条为“表示原则:把知识叠入数据以求逻辑质朴而健壮”。结合之前自己的一些经验,我对这个原则很有共鸣,所以先学习了数据驱动编程相关的内容,这里和大家分享出来和大家一起讨论。9 p3 v# Q# a& Q, E0 d2 g+ ?6 W3 L) ]
0 M: y/ e& I% i
数据驱动编程的核心1 P# A$ P! U; m& z. L$ E; ~

! I; f  W* a+ m+ L. E' X数据驱动编程的核心出发点是相对于程序逻辑,人类更擅长于处理数据。数据比程序逻辑更容易驾驭,所以我们应该尽可能的将设计的复杂度从程序代码转移至数据。' p* _' F7 l: S1 V% }9 Q( i" p# b
: H% _  [( S. K7 R. d' [
真的是这样吗?让我们来看一个示例。* s+ s" h" ^+ F6 `8 J8 l

2 g9 ?( P& f+ T: Z假设有一个程序,需要处理其他程序发送的消息,消息类型是字符串,每个消息都需要一个函数进行处理。第一印象,我们可能会这样处理:
0 j: T, n: R4 `void msg_proc(const char *msg_type, const char *msg_buf) ( n9 d: e7 @* k
{ 0 |, p0 ]4 R0 ~% Y8 m
    if (0 == strcmp(msg_type, "inivite")) ( }9 n: r7 _. V9 m& P) M
    { ) R9 h+ U0 n# @5 R2 g# f
        inivite_fun(msg_buf); 7 G2 L0 j* y2 W5 ]
    } 5 R: [4 @% W+ g- J5 }. E
    else if (0 == strcmp(msg_type, "tring_100"))
: W) ^. h5 k, B! l# M/ i% T    { 6 I+ @2 d9 m5 c: c1 m
        tring_fun(msg_buf); , ~: S$ ~7 |$ v  x
    }
0 t+ s/ |% [8 s    else if (0 == strcmp(msg_type, "ring_180"))
) K$ m" K( {0 C! {9 W    {
8 T4 R" N+ B$ x4 L        ring_180_fun(msg_buf);
" a6 @  Q: W9 c- a+ P  @! o    }
! \/ }) w6 {% C6 h9 B  d    else if (0 == strcmp(msg_type, "ring_181"))
7 s6 u7 j  R8 g1 r' S- [    { " [; \# F- T, }* ]8 a5 \; E# m
        ring_181_fun(msg_buf); 4 l& z& u! a; x: S* D/ M! O
    }
1 Y0 z2 r$ m6 j' D' L    else if (0 == strcmp(msg_type, "ring_182"))
, o5 t+ v# t1 k7 A8 A9 {    { 6 v/ }; `% n1 T% O* B
        ring_182_fun(msg_buf);
2 P7 g6 \4 y: c( [2 N3 Z: V* T    } : y! `9 [* E6 q+ ?- P) U* l8 x7 M
    else if (0 == strcmp(msg_type, "ring_183")) ! h7 x8 t- V  p# J: c9 m7 A8 m5 u8 N
    { ; G. H: }4 @2 h- l! e2 m7 c3 o
        ring_183_fun(msg_buf); ! p- R7 e9 v- F
    }
* f1 l" ?+ ]5 \( e' B    else if (0 == strcmp(msg_type, "ok_200"))
& l4 s+ O7 Y" u! D' d! y    {   F( x1 S0 N6 D' G
        ok_200_fun(msg_buf); ! _1 i, ?, r/ e; w
    }
9 e1 ?) `$ b5 H/ D1 _7 @; F4 h
& `6 {, T) W; U7 {, J0 k    。。。。。。
+ Y/ y' u6 w# l    else if (0 == strcmp(msg_type, "fail_486"))
7 _6 k7 ]! I. j9 [& R. I2 |. N    { % \3 G' u0 o' U% z: h" o; n
        fail_486_fun(msg_buf); + B+ p5 ]; W5 C$ y1 s2 e* b# G
    } 9 W9 T6 F! K0 I! P
    else 5 N0 Z+ w# ]( p/ O! i
    {
8 D$ n" _2 B/ Q  p        log("未识别的消息类型%s\n", msg_type);
) y- Y# V' H' f# m/ q' K0 t9 g    }
8 s) L/ u" {% u3 r: F} - t: s  E# y1 G2 A* k
上面的消息类型取自sip协议(不完全相同,sip协议借鉴了http协议),消息类型可能还会增加。看着常常的流程可能有点累,检测一下中间某个消息有没有处理也比较费劲,而且,没增加一个消息,就要增加一个流程分支。4 Y- {- h( z4 @, n" V$ t  C

; y% I8 z" q! Q, ~. D/ t; S按照数据驱动编程的思路,可能会这样设计: ! \8 n2 V  F! E1 @$ i0 I3 ]/ X
typedef void (*SIP_MSG_FUN)(const char *);: E0 @& o2 ~$ k! Y0 E* W
# W3 N4 a3 {0 R, y4 D4 K# _1 T; z+ P/ G
typedef struct __msg_fun_st
/ A- W& ^  `) H4 G; D1 e4 l{
3 J9 Y. f) I# f# w& N- A    const char *msg_type;//消息类型 0 i6 t$ v; p& z1 g7 C
    SIP_MSG_FUN fun_ptr;//函数指针 ' w9 c$ _  M( y& \
}msg_fun_st;) E0 a% x, y' Z: y; S- L$ v" Z) H* P

4 t7 g: F0 b* D/ W9 j; W4 [7 umsg_fun_st msg_flow[] = 4 f7 |2 A, I) @6 W8 u
{ & X' b8 y4 C+ Y" w" m/ {. O. k
        {"inivite", inivite_fun},
) X0 D2 H/ h% o5 a2 L6 n        {"tring_100", tring_fun}, 6 N5 M! r5 I2 `) r9 Q4 E
        {"ring_180", ring_180_fun},
- y# M% H0 H7 d' v+ I; h        {"ring_181", ring_181_fun}, 3 N: t# W. }4 }6 E
        {"ring_182", ring_182_fun},
2 W* u! `# H/ k% f        {"ring_183", ring_183_fun}, 5 b0 W4 G. n- r( e9 H1 y4 ^* r
        {"ok_200", ok_200_fun},% X+ f2 V7 ?6 H

: k7 K/ j% u/ `0 n$ E3 r3 `        。。。。。。
6 u3 S3 q/ S) M3 p2 J9 b( ]6 I        {"fail_486", fail_486_fun} 1 P. {  `3 k" T4 ~  e
};
8 v' X2 R9 U; h) ?# Y& m! M4 g2 r) A2 i! k6 U$ M
void msg_proc(const char *msg_type, const char *msg_buf) 0 N; T9 |+ ]. p& N8 i
{
6 k2 E( X5 V* B" {) \6 l, n    int type_num = sizeof(msg_flow) / sizeof(msg_fun_st);
5 v2 j. ]4 d' v. U% r% C# W) E7 N    int i = 0;
( ?0 M4 ~' X( y+ r8 ]8 G+ P
7 e/ T7 g' R0 r, [$ E" ?    for (i = 0; i < type_num; i++) ( v" d" [% S- P8 I% ?
    {
( R  v+ }3 G# q/ R( W  ]6 s        if (0 == strcmp(msg_flow.msg_type, msg_type))
$ y. }' z7 e( s9 k. ^" }# N        { ( J1 v9 J+ P% V5 c1 G, L( d
            msg_flow.fun_ptr(msg_buf);
8 U1 a5 s6 i% Q* K' K, j3 l- s$ t            return ; - i; A9 u! j" [$ O7 d/ z
        } 9 Y" w3 l" t/ b& |2 m5 G
    }
1 K. c1 d" u, Z    log("未识别的消息类型%s\n", msg_type); ' {: _+ C" m7 n2 |
}
( V9 N0 i2 p) ^3 _# b# r+ o" C* H/ p+ o; @
下面这种思路的优势:
. {9 h5 C+ u) A' ]+ A$ o; l1 w  r' I4 F/ P
1、可读性更强,消息处理流程一目了然。
" ^$ S4 t8 ]* L* H" L1 }5 Y
0 j( t+ w% F/ V* x2、更容易修改,要增加新的消息,只要修改数据即可,不需要修改流程。' `, }% E( n; _5 M( L7 C* u
9 d, L9 ]. x! ?; e2 @5 c) |  R0 a
3、重用,第一种方案的很多的else if其实只是消息类型和处理函数不同,但是逻辑是一样的。下面的这种方案就是将这种相同的逻辑提取出来,而把容易发生变化的部分提到外面。
5 n& d4 m6 I" a# Y
: n2 y% P$ i- s' Z+ R, H隐含在背后的思想:0 T$ m9 S( H7 N
$ t1 R4 \4 }* n$ \) d6 k% |/ |
很多设计思路背后的原理其实都是相通的,隐含在数据驱动编程背后的实现思想包括:
1 G2 n5 A* G# X
4 F# y' |* f- g! M' s1、控制复杂度。通过把程序逻辑的复杂度转移到人类更容易处理的数据中来,从而达到控制复杂度的目标。9 U' ~$ R! l& D" r/ j6 R

  i* ^& o9 R' L2、隔离变化。像上面的例子,每个消息处理的逻辑是不变的,但是消息可能是变化的,那就把容易变化的消息和不容易变化的逻辑分离。
) H4 ]6 v$ j6 D+ p# W% A& B: ?5 S; D. F
1 Z5 Y. B' z: c9 h: l$ ^% J) e3、机制和策略的分离。和第二点很像,本书中很多地方提到了机制和策略。上例中,我的理解,机制就是消息的处理逻辑,策略就是不同的消息处理(后面想专门写一篇文章介绍下机制和策略)。% X; e$ Z. \% U6 A+ K$ x

/ g' S5 U; x) ]$ G5 M2 o" |数据驱动编程可以用来做什么:4 U8 H" A8 w* m+ ?3 M
) h8 U- H! H5 m- D
如上例所示,它可以应用在函数级的设计中。/ @" {1 c1 O; t0 l  [' `* f: Z
9 X1 o; I& b1 L" x" _4 F" J7 F
同时,它也可以应用在程序级的设计中,典型的比如用表驱动法实现一个状态机(后面写篇文章专门介绍)。( H; a" k* o1 P2 v& r( |' D

* u. {# e8 q7 y也可以用在系统级的设计中,比如DSL(这方面我经验有些欠缺,目前不是非常确定)。; t( W8 I  h0 _" a3 B, L7 u# l8 P9 e3 k
1 E6 E& p; R0 W2 J! p
它不是什么:$ B" s& f7 n& Q6 k$ ?

5 L' t3 C0 Q, I" Q1、 它不是一个全新的编程模型:它只是一种设计思路,而且历史悠久,在unix/linux社区应用很多;
( N' Y& i- F3 d; m: |' s5 g2 N* [8 t
2、它不同于面向对象设计中的数据:“数据驱动编程中,数据不但表示了某个对象的状态,实际上还定义了程序的流程;OO看重的是封装,而数据驱动编程看重的是编写尽可能少的代码。”4 I' z' a* q7 J! T6 Q- n
# h: \5 D; |* i# i# Z/ {
书中的值得思考的话:0 ]' y& B2 M" O0 F& f
; L6 D0 J0 ?- k9 C- e/ \
数据压倒一切。如果选择了正确的数据结构并把一切组织的井井有条,正确的算法就不言自明。编程的核心是数据结构,而不是算法。——Rob Pike
3 p1 H) S9 s5 q( }3 v  M5 A+ k5 W5 x+ h: \' u
程序员束手无策。。。。。只有跳脱代码,直起腰,仔细思考数据才是最好的行动。表达式编程的精髓。——Fred Brooks
$ o6 v/ T7 k; [1 d- @0 Q5 S
( N" Z3 R" S, n+ {/ V数据比程序逻辑更易驾驭。尽可能把设计的复杂度从代码转移至数据是个好实践。——《unix编程艺术》作者。

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-11-25 22:20 , Processed in 0.156250 second(s), 23 queries , Gzip On.

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

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

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