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

什么是数据驱动编程

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
! h' n. d  u! K+ q- a
前言:
! G" c! n, Z! ^# A$ b( [
* ?5 C4 o8 h; ^最近在学习《Unix编程艺术》。以前粗略的翻过,以为是介绍unix工具的。现在认真的看了下,原来是介绍设计原则的。它的核心就是第一章介绍的unix的哲学以及17个设计原则,而后面的内容就是围绕它来展开的。以前说过,要学习适合自己的资料,而判断是否适合的一个方法就是看你是否能够读得下去。我对这本书有一种相见恨晚的感觉。推荐有4~6年工作经验的朋友可以读一下。
) Y. J/ p5 @; J  {
8 P7 {! ~4 W, d+ H# m2 Z0 J* [正题:/ K' L" R8 d& g0 E; \6 C/ X: Q
7 G/ Y6 Z5 e! U& r
作者在介绍Unix设计原则时,其中有一条为“表示原则:把知识叠入数据以求逻辑质朴而健壮”。结合之前自己的一些经验,我对这个原则很有共鸣,所以先学习了数据驱动编程相关的内容,这里和大家分享出来和大家一起讨论。. Y* x& L" ~4 L- w, {
  ?+ j9 y$ p  }
数据驱动编程的核心
. B8 T+ S! \+ D% q& M) h. Z6 f: F+ \0 A3 R9 X/ k( |/ ^2 F$ R
数据驱动编程的核心出发点是相对于程序逻辑,人类更擅长于处理数据。数据比程序逻辑更容易驾驭,所以我们应该尽可能的将设计的复杂度从程序代码转移至数据。: s" O" Q' O2 E9 d1 e5 n: q
) H3 z% x1 i1 l1 v* z3 }
真的是这样吗?让我们来看一个示例。
: Z: A% Q! Q9 `  {
, g! A9 A8 o4 m0 A* n- U7 M3 S假设有一个程序,需要处理其他程序发送的消息,消息类型是字符串,每个消息都需要一个函数进行处理。第一印象,我们可能会这样处理: * P: f* ^7 L7 I" G; V6 f
void msg_proc(const char *msg_type, const char *msg_buf)
8 C7 Q! [4 b0 u) |# E{ 7 _% b0 `' M0 ]- U
    if (0 == strcmp(msg_type, "inivite")) 3 \9 W0 }% T6 Q2 X2 g' y3 f: O1 i
    { ) f% t# D1 g9 k% U4 n8 Z) f
        inivite_fun(msg_buf); / ^6 \" [+ K$ n7 `5 B, w' Y
    }
# j/ w$ ?. U7 K" t9 t  y( N) ?    else if (0 == strcmp(msg_type, "tring_100"))
$ V# `; x' j4 c, H5 a/ h    { 0 B" {7 g3 p4 ?0 t" i
        tring_fun(msg_buf);
! p  b7 Y; H% ~- B! t  z  U3 k    } " @4 T/ {% e& F
    else if (0 == strcmp(msg_type, "ring_180")) : G% a+ X. Q: Q/ M+ G" y
    {
* w, @" b7 }. j, Y        ring_180_fun(msg_buf); / D- Q; ^6 d6 f' ?3 D
    }
4 w- \8 ^, O$ O% L    else if (0 == strcmp(msg_type, "ring_181")) 4 K  l# i7 q: u5 [9 r1 r+ N7 N
    {
' Y5 I1 V2 }3 _3 Y  ~/ ?        ring_181_fun(msg_buf); : K9 g* U+ I9 |- R
    }
9 V! s" Z( X$ z. z% e    else if (0 == strcmp(msg_type, "ring_182"))
9 \! C! f: \, F# p" W1 @3 T    {
2 R9 t) h% w5 {9 W& {2 y        ring_182_fun(msg_buf);
8 }* N* Y2 {' o' x, B. C  g    }
3 ^8 L  [, R( W1 P8 ]    else if (0 == strcmp(msg_type, "ring_183"))
+ i7 ]' `, E1 }7 g    { 1 T( \& c/ I' V  B( @2 E
        ring_183_fun(msg_buf);
" ^- s4 o, L7 e0 f1 b$ P    } 2 \; W0 B( t6 ?7 ?) r4 {9 b
    else if (0 == strcmp(msg_type, "ok_200"))
6 t# B& X. }) ^6 @. j8 L    { * A! N! P8 d, Z# w
        ok_200_fun(msg_buf);
9 U9 I" ?5 l0 Z2 k+ s+ J    }) ^! z2 W* N) c4 h# e6 @6 g) Z* H

) ~8 o6 U* ]- Q* m* o6 P    。。。。。。
7 a# Q1 D2 j+ V* k+ v# k! U    else if (0 == strcmp(msg_type, "fail_486"))
5 |+ c' u. t% r; B- \# [    {
; u$ c" p' c2 I. F$ Z" ?2 `# B1 m4 Q        fail_486_fun(msg_buf);
8 s8 P; p+ j+ f0 U$ [' S    } / T. I$ U7 t0 K
    else , y$ _  h) }$ V  W9 i0 b
    {
/ H' [2 X  X' N3 [5 l        log("未识别的消息类型%s\n", msg_type);
  Y0 j6 h$ j) U* U    } $ b# S( G$ G+ y# ~8 Y: j
}
1 R- x* e# T, l9 Y上面的消息类型取自sip协议(不完全相同,sip协议借鉴了http协议),消息类型可能还会增加。看着常常的流程可能有点累,检测一下中间某个消息有没有处理也比较费劲,而且,没增加一个消息,就要增加一个流程分支。
+ I- r% X- ]' w( w/ m( b
. H! d; u& |+ y( T$ A按照数据驱动编程的思路,可能会这样设计: 0 u7 v. h- W6 k
typedef void (*SIP_MSG_FUN)(const char *);
9 [# q  E% n# S
2 Z9 W% D& `$ n' |# etypedef struct __msg_fun_st . ~, s! k) t% ?4 K! I! v
{
' z5 o& R3 V9 O6 E) ?    const char *msg_type;//消息类型
+ ~5 q. A7 v* t, j4 n( g1 S! i# X    SIP_MSG_FUN fun_ptr;//函数指针 ) Z$ y& z, s' L) \5 `+ |2 g
}msg_fun_st;
! g; p- m* B( n7 e+ V9 D3 D
" D7 ]  l: n/ `: @msg_fun_st msg_flow[] =
% \8 L2 K) f6 }4 }- ?{ " u& G/ g$ t1 }4 u4 X5 p  q
        {"inivite", inivite_fun},
2 E: q% q' e4 l' v% [- o8 C* q, Y        {"tring_100", tring_fun},
  \, ]0 Z0 Z! D  N        {"ring_180", ring_180_fun}, 6 E  n# ~& i- y+ O+ v+ P1 y9 R3 ]
        {"ring_181", ring_181_fun},
6 H0 a- Q* K$ _& K        {"ring_182", ring_182_fun},
. j4 w5 m9 C; y1 T8 W! [  a        {"ring_183", ring_183_fun},
8 m' Y& O8 L9 x/ ]% B; }2 g        {"ok_200", ok_200_fun},: U9 j7 S7 D. X+ \% H3 o8 J9 I
8 X# t" I  |2 F+ e6 J; c
        。。。。。。
( Q( `% O5 I7 V" g% p; n$ ]        {"fail_486", fail_486_fun} # ~$ R: [/ S5 s+ |7 N
};
* P  Z( l7 I4 }/ D1 R2 p) j$ S+ O3 F! T4 I  n, v; U0 _
void msg_proc(const char *msg_type, const char *msg_buf) ( _' g' a  H* H1 f
{
% Z5 w2 K; b' u7 Y    int type_num = sizeof(msg_flow) / sizeof(msg_fun_st); ! o+ |1 y0 [" k' R8 D
    int i = 0;
4 L# R8 E# i' V! K* P5 A3 e3 J
; ~4 I7 v$ n! L# J    for (i = 0; i < type_num; i++)
- b7 a" d7 X  }8 ?    { + h$ ]  A: Z; x5 d* P
        if (0 == strcmp(msg_flow.msg_type, msg_type)) ' ?! q+ V* o9 |+ V* w
        {   R) e- T0 `4 H/ j5 T& A+ @  J
            msg_flow.fun_ptr(msg_buf);
: Z# q8 G/ E0 K$ b1 ^            return ; * u# h7 |5 e9 Q, _5 S
        } 7 D0 D7 w* j2 l8 M! R: e
    }
) K  W0 L, l; o7 P5 B$ s* z    log("未识别的消息类型%s\n", msg_type); 2 s7 I2 x) |+ ^) X7 N, j! K. d' h2 M
} : a9 s3 z8 u% I, m0 _
( f4 }/ }( v! X
下面这种思路的优势:% ?9 I! ]$ q5 b1 C) \  J

; J" S& J2 u9 ?; k$ A2 |/ j$ l* \! G( |* C1、可读性更强,消息处理流程一目了然。
5 t% L5 G  Y* `- t  H3 {' S5 u* L# F* P* O) B9 C) u
2、更容易修改,要增加新的消息,只要修改数据即可,不需要修改流程。
8 ?( U# ]  t4 i) T- w* V" @) l& L
3、重用,第一种方案的很多的else if其实只是消息类型和处理函数不同,但是逻辑是一样的。下面的这种方案就是将这种相同的逻辑提取出来,而把容易发生变化的部分提到外面。8 k& f" f4 H& E, L* H

. H8 Z' Z3 R9 d$ I+ j隐含在背后的思想:
# W4 @, E9 h, y0 c& L7 t2 ]
, ^" u  ]6 S( L# S  X: Q2 U很多设计思路背后的原理其实都是相通的,隐含在数据驱动编程背后的实现思想包括:5 f# ^9 ?2 h2 E# O" `7 d& w
  [# U8 a3 Y5 D  P9 F
1、控制复杂度。通过把程序逻辑的复杂度转移到人类更容易处理的数据中来,从而达到控制复杂度的目标。! ]+ n" h# q2 n0 ?5 _. r. p
0 g7 Q' s' S5 k4 C3 m- q' ^5 F
2、隔离变化。像上面的例子,每个消息处理的逻辑是不变的,但是消息可能是变化的,那就把容易变化的消息和不容易变化的逻辑分离。* l9 r4 _( D  ]; h, P) n# `

/ h1 H! G) `1 ~# H$ z3、机制和策略的分离。和第二点很像,本书中很多地方提到了机制和策略。上例中,我的理解,机制就是消息的处理逻辑,策略就是不同的消息处理(后面想专门写一篇文章介绍下机制和策略)。
5 Z: E% K% V, r4 C/ U) @$ y6 Z
( G* j5 X) C  I, g数据驱动编程可以用来做什么:4 K' c: ~  f- v2 P* d+ R

- t8 N* z8 J4 o$ ?如上例所示,它可以应用在函数级的设计中。
" g/ p# t: K& m+ p" ^  o+ B4 k9 O9 h2 E  u0 u6 C4 z
同时,它也可以应用在程序级的设计中,典型的比如用表驱动法实现一个状态机(后面写篇文章专门介绍)。
0 h/ T- o+ i+ n9 D
# N' O6 {+ Z1 f4 Q. d也可以用在系统级的设计中,比如DSL(这方面我经验有些欠缺,目前不是非常确定)。0 H6 I; N, s4 E) p  \

) |% n( _! m' m5 C& x& s它不是什么:
4 v) J' D3 @: ]/ {4 U+ p4 @# b3 g0 z+ v* G( [
1、 它不是一个全新的编程模型:它只是一种设计思路,而且历史悠久,在unix/linux社区应用很多;$ V7 s4 c0 ]- {

1 k8 X9 W( s$ Q- g3 |& a2、它不同于面向对象设计中的数据:“数据驱动编程中,数据不但表示了某个对象的状态,实际上还定义了程序的流程;OO看重的是封装,而数据驱动编程看重的是编写尽可能少的代码。”6 ?) A6 M9 a, c! n# f# k; q9 q  K( N
; w) p8 O* s' J6 b! k+ x" C- a* E
书中的值得思考的话:
- b6 z' M* l" s' q1 v
: J9 i$ H( W6 G1 a数据压倒一切。如果选择了正确的数据结构并把一切组织的井井有条,正确的算法就不言自明。编程的核心是数据结构,而不是算法。——Rob Pike) T/ t: P! T5 z9 }' a5 V( p7 S( f

8 H2 Y$ K$ o5 b8 r+ z程序员束手无策。。。。。只有跳脱代码,直起腰,仔细思考数据才是最好的行动。表达式编程的精髓。——Fred Brooks7 o' N0 a5 X/ q( A

" k9 x, v/ d3 r/ w8 n7 C数据比程序逻辑更易驾驭。尽可能把设计的复杂度从代码转移至数据是个好实践。——《unix编程艺术》作者。

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

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

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

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

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