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

什么是数据驱动编程

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
1 B4 t- C, k+ Z) J
前言:% D! ~! {" S  D
6 }' B) g  r4 v$ g7 V6 a) P
最近在学习《Unix编程艺术》。以前粗略的翻过,以为是介绍unix工具的。现在认真的看了下,原来是介绍设计原则的。它的核心就是第一章介绍的unix的哲学以及17个设计原则,而后面的内容就是围绕它来展开的。以前说过,要学习适合自己的资料,而判断是否适合的一个方法就是看你是否能够读得下去。我对这本书有一种相见恨晚的感觉。推荐有4~6年工作经验的朋友可以读一下。7 F+ b! `4 s! K5 i- ]
, P7 V4 F4 I% A7 M
正题:; c9 @/ G/ S7 \+ T& b8 g' e* J
! G; A, D( _. o
作者在介绍Unix设计原则时,其中有一条为“表示原则:把知识叠入数据以求逻辑质朴而健壮”。结合之前自己的一些经验,我对这个原则很有共鸣,所以先学习了数据驱动编程相关的内容,这里和大家分享出来和大家一起讨论。
$ D0 @) H, ]( d: z* v$ S; I, I* k7 n7 H' U8 R8 c* d
数据驱动编程的核心+ ~' g0 T# h% X9 P1 S* F7 b7 C
/ x% f. D; K; d) I5 A$ x- S
数据驱动编程的核心出发点是相对于程序逻辑,人类更擅长于处理数据。数据比程序逻辑更容易驾驭,所以我们应该尽可能的将设计的复杂度从程序代码转移至数据。
/ }' C& j: y. l, N& }  }) W3 ?, N$ H- k' Z
真的是这样吗?让我们来看一个示例。  ^# g" A5 }; M; h  c8 z& ?
6 A* o$ t) ^" b0 p& ?" B
假设有一个程序,需要处理其他程序发送的消息,消息类型是字符串,每个消息都需要一个函数进行处理。第一印象,我们可能会这样处理:
" f4 k* A% g3 Y* i' ?void msg_proc(const char *msg_type, const char *msg_buf)
: ^- j2 c: s7 D6 K7 W+ ]{ 8 h) a  {/ w) R" k+ @
    if (0 == strcmp(msg_type, "inivite"))
! e( J9 i' m  ?, |! B+ J    {
) G9 E% p( n, x# R3 {- d# {        inivite_fun(msg_buf); 1 S% ?5 l2 G  q
    } 4 ]; ^, K4 J( q+ t) H
    else if (0 == strcmp(msg_type, "tring_100"))
# P  w1 A4 E2 v    {
5 w+ K, ], y# |9 l# p2 {2 S        tring_fun(msg_buf); 9 m4 e5 n, b. T: |2 W
    } % C/ j0 e1 k3 Y) n
    else if (0 == strcmp(msg_type, "ring_180")) : {& R' d& A3 T9 q" p5 w8 g6 ]5 _
    {
$ A$ M( P& E% L; w$ t* ?6 _        ring_180_fun(msg_buf);
1 ^7 w( m. W7 V/ c* |( ~    }
/ D9 I* j: Q& C: ^  E) D9 s3 O    else if (0 == strcmp(msg_type, "ring_181")) 2 B9 g& y8 x' I
    {
) N/ j/ E4 k! j1 p! {        ring_181_fun(msg_buf); 8 k9 b0 T* Z9 j! B! b; L+ _
    }
* s4 V' E6 a7 X( B4 ?    else if (0 == strcmp(msg_type, "ring_182")) 1 ~0 g, U9 X2 @; H2 i
    { 9 |+ h( C/ a  v( |3 ~
        ring_182_fun(msg_buf); 5 v0 K0 p$ v; a% r& ]
    } 3 S+ b* u, y; h, H& j
    else if (0 == strcmp(msg_type, "ring_183"))
: U% K# r4 k9 O    { ) [$ Y+ b$ G+ p) s! z, G
        ring_183_fun(msg_buf); / ?+ A/ T" O. {6 z1 p  s( G% q" }
    } / a5 c* w5 B1 m- x# s
    else if (0 == strcmp(msg_type, "ok_200"))
9 u- q3 m/ b& ~  H+ o    {
  h& I" X+ _! t" [" A; M* F2 @        ok_200_fun(msg_buf);
  D: L7 \0 D+ E9 o/ W" y& W3 I    }+ z" x$ ?* q( r* Y$ [! D
, E6 z- \3 F4 v# @
    。。。。。。 2 j- D! e' V% b& k8 a& |
    else if (0 == strcmp(msg_type, "fail_486"))
% V( F$ H% z# I' s& J    { & k' h" P, e/ G
        fail_486_fun(msg_buf);
+ ~" r" t# J, r    }
6 P3 S3 \* E7 T+ N' q3 H8 u    else / U2 @' \2 @- k) G' ~
    {
5 r5 {# P$ a5 V5 N        log("未识别的消息类型%s\n", msg_type); & Y) r2 a/ w' b$ ^2 o: B( G( B
    }
% |9 E2 k, S' V  y, q  a} 5 h( r* v0 z+ |0 v  P
上面的消息类型取自sip协议(不完全相同,sip协议借鉴了http协议),消息类型可能还会增加。看着常常的流程可能有点累,检测一下中间某个消息有没有处理也比较费劲,而且,没增加一个消息,就要增加一个流程分支。
8 H1 W: b% ~" r
* q3 Q  d/ a8 i8 c& Z按照数据驱动编程的思路,可能会这样设计:
4 y3 X# O1 {# E. L6 o1 ], Ltypedef void (*SIP_MSG_FUN)(const char *);
1 L( `! i( |  Z) y2 W6 H9 M; P4 a4 ^# b8 D7 A6 ~
typedef struct __msg_fun_st
2 ~, L' k2 @* A& ~; g{
) ]' [1 J2 h# O" j( I8 T    const char *msg_type;//消息类型
+ s0 P3 b& E: J4 G7 d" n5 A    SIP_MSG_FUN fun_ptr;//函数指针
$ l7 e  y+ b/ F0 g' j! Z) f}msg_fun_st;
0 Q9 J3 p6 p: i
0 E! p/ X& w5 G+ c& ~  T% Nmsg_fun_st msg_flow[] = + L, R3 P, v' d; |% J) R
{ " P1 R. M* a" ~* D% {
        {"inivite", inivite_fun}, + A6 e" W" x6 Z6 B2 _: z  n0 E
        {"tring_100", tring_fun},
+ P+ b4 m9 x$ L3 }        {"ring_180", ring_180_fun},   z6 b, T; e" E7 f# j: A
        {"ring_181", ring_181_fun},
6 c; m; w3 G/ N1 P1 k        {"ring_182", ring_182_fun}, * L+ t- Y: m9 \( Q) {( U
        {"ring_183", ring_183_fun},
. H  Z; ^. H0 w7 i        {"ok_200", ok_200_fun},
- _0 W- |  E7 E7 W2 P, f0 g/ V5 N+ R0 ?" B
        。。。。。。
0 m4 U- z- p2 Q; _+ t) [! k. u        {"fail_486", fail_486_fun}
: w+ Y& d7 G' w8 h};0 b6 C% d& y8 d

$ I* N& U" y8 @3 g7 gvoid msg_proc(const char *msg_type, const char *msg_buf) 7 D5 }5 p/ Y( a2 N# C% {
{
3 Q- t  r9 U8 h: T- p9 Z+ T    int type_num = sizeof(msg_flow) / sizeof(msg_fun_st); 3 f* |3 @# c: r& O) H& q. R. x) U
    int i = 0;/ s% F5 f$ ]$ _
0 M7 T6 ?( c8 J' f# K* `2 x/ p
    for (i = 0; i < type_num; i++) : g7 G0 W# f9 g8 Z# X
    { % X0 t% i, f" }( p" ]1 c4 m4 I
        if (0 == strcmp(msg_flow.msg_type, msg_type)) 9 u! _0 R9 h' E" u- w; x2 N* Z) F
        { % j8 ~- f1 ], n6 O6 z) A1 z
            msg_flow.fun_ptr(msg_buf); - z/ Z5 _; b2 d! t/ @  L# G
            return ;
+ J' b$ ~* V  L0 S        }
9 g& ~" _" R1 \* ~$ O    } - o& `7 J9 s. @; P
    log("未识别的消息类型%s\n", msg_type);
5 b  f( p. P" I} 1 v  v. H7 _$ @4 j! H

9 ]4 O$ M- Y, R: p  V- |# F下面这种思路的优势:% o% e8 ~6 Z; ]

5 [+ Y- ~* w; l( M! F: h0 ?1、可读性更强,消息处理流程一目了然。1 ^( g0 r1 J  o5 B+ y! T! ~

* r6 V5 z5 J8 P  A3 `2、更容易修改,要增加新的消息,只要修改数据即可,不需要修改流程。
$ ?2 T; q0 \, Q1 D6 `! g; [' x4 Z- `4 H# p; Q+ l) W
3、重用,第一种方案的很多的else if其实只是消息类型和处理函数不同,但是逻辑是一样的。下面的这种方案就是将这种相同的逻辑提取出来,而把容易发生变化的部分提到外面。- l8 _" _/ A; u. J3 ]% j) V$ Z

  N" r1 i* K' q$ T3 B隐含在背后的思想:
" T8 J! i3 m9 P2 e! V7 r! ?. C! h7 ~: Q0 H" F4 Q& i
很多设计思路背后的原理其实都是相通的,隐含在数据驱动编程背后的实现思想包括:& X* b  \8 C1 u) x4 `8 h! Z% I

3 a* N! T4 t, M1、控制复杂度。通过把程序逻辑的复杂度转移到人类更容易处理的数据中来,从而达到控制复杂度的目标。
! b1 S5 X5 ^: W! |) F5 U
: \. P( A" E; J2、隔离变化。像上面的例子,每个消息处理的逻辑是不变的,但是消息可能是变化的,那就把容易变化的消息和不容易变化的逻辑分离。
- c' k# `& r3 j: ~/ g5 ?4 g7 k* A* j  C# N$ u/ F" |
3、机制和策略的分离。和第二点很像,本书中很多地方提到了机制和策略。上例中,我的理解,机制就是消息的处理逻辑,策略就是不同的消息处理(后面想专门写一篇文章介绍下机制和策略)。
5 `- F& h6 Y  ]' x" ]6 T& Q. `+ G4 L
数据驱动编程可以用来做什么:3 n6 i2 z3 u- y: t
2 e9 m$ Y  k# Q0 j1 P4 ~1 N* `
如上例所示,它可以应用在函数级的设计中。) Q( t& `8 W' q7 |, L

" z  j1 k( g9 q3 O, u- |! \同时,它也可以应用在程序级的设计中,典型的比如用表驱动法实现一个状态机(后面写篇文章专门介绍)。
+ E$ u+ D' W/ b% m! f) Y# L* Z3 Q4 o4 |/ P% Q( H
也可以用在系统级的设计中,比如DSL(这方面我经验有些欠缺,目前不是非常确定)。
# j$ c- i) Z9 U7 q
+ O- [9 _$ H9 [" N+ V, ^/ n" x4 G它不是什么:
3 H5 U4 k# c9 G$ k( G& D+ k# D, |# u8 s# z+ a8 R
1、 它不是一个全新的编程模型:它只是一种设计思路,而且历史悠久,在unix/linux社区应用很多;+ y% l+ K# v! I0 w) C
. Y* ?9 h( i" T* b3 J- H! m  A
2、它不同于面向对象设计中的数据:“数据驱动编程中,数据不但表示了某个对象的状态,实际上还定义了程序的流程;OO看重的是封装,而数据驱动编程看重的是编写尽可能少的代码。”" ~3 l1 X6 ?+ d" t! |( f
+ O1 w. P: K  ~- y) E
书中的值得思考的话:6 S+ B& y0 M1 [% A, d! o% O
6 x: c) v& {+ n0 F( E
数据压倒一切。如果选择了正确的数据结构并把一切组织的井井有条,正确的算法就不言自明。编程的核心是数据结构,而不是算法。——Rob Pike% `( @: n! l0 C2 @% e

6 W# u. W, l: N' k6 P7 l程序员束手无策。。。。。只有跳脱代码,直起腰,仔细思考数据才是最好的行动。表达式编程的精髓。——Fred Brooks
+ m# i" C! A( T& {- u. R+ X, ?' t; H
数据比程序逻辑更易驾驭。尽可能把设计的复杂度从代码转移至数据是个好实践。——《unix编程艺术》作者。

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-11-26 01:14 , Processed in 0.156250 second(s), 24 queries , Gzip On.

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

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

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