ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

数据流图DFD入门:四类符号、分层画法与常见误区

数据流图DFD入门:四类符号、分层画法与常见误区

简介:这份PPT课件面向系统分析与设计入门者及管理信息系统课程学习者,围绕数据流图(DFD)的绘制方法与实例展开,帮助非技术背景读者理解系统信息流动与逻辑功能。内容从四类基本符号切入,讲清数据流、加工、数据存储与外部项的含义及命名编号规则,并梳理识别外部项、确定输入输出、划分主要处理功能、自顶向下逐层分解、检查完善等绘制步骤,还补充绘制原则、常见注意事项以及银行取款处理等实例,便于对照理解关联图、顶层图与分层图的展开过程。资源包仅含1个PPT文件,大小约312KB,以课件形式集中呈现知识点、符号示例与案例图示,适合课堂讲授、复习或自学时快速查阅。目前已有626人学习,可作为掌握DFD基础与实例演算的轻量参考资料。

1. 数据流图到底是什么:一张只画“信息怎么走”的图

装修过房子的人都见过两种图:一种是效果图,告诉你墙刷什么颜色、沙发摆哪里;另一种是水电走向图,只画线从哪儿进、经过哪个阀门、最后通到哪个水龙头,不管瓷砖什么牌子。数据流图就是软件系统里的那张“水电走向图”——它只画信息从哪儿来、中间被谁处理了一下、最后流到哪儿去、在哪个地方存着,至于用什么语言写、跑在什么机器上、数据库是哪一家,一律不管。

所以“数据流图”这四个字,拆开看很直白:数据,是流动的东西;流,是方向;图,是画出来的样子。它不承诺性能,不承诺界面,只承诺一件事——把系统在信息处理上“要做什么”说清楚。恰恰因为说得不细,不懂技术的人也能看懂,它才成了需求沟通里最常被拿出来的一张纸。

2. 为什么需要数据流图:把“要做什么”和“怎么做”分开

很多人第一次接触数据流图,会犯一个很自然的错误:把它当成架构图或者部署图。于是图上开始出现服务器、出现表结构、出现接口地址,画着画着就变成了一张谁也看不懂的蜘蛛网。要避免这个坑,得先明白它到底在哪个层面上说话。

2.1 它描述的是一套逻辑模型,不是物理实现

数据流图(DFD,Data Flow Diagram)在定义上就是“组织中信息运动的抽象”,是信息逻辑系统模型的主要形式。这句话有点绕,翻译成大白话:它抽掉了一切物理细节,只留下“信息的运动”。

具体抽掉了哪些东西?硬件、软件、数据结构、文件组织,统统不要。也就是说,图上不出现“这台服务器”“那张表有几个字段”“这个接口用什么协议”,也不出现“先执行哪一步、后执行哪一步”。它描述的是一套与物理描述无关的逻辑功能,回答的是“这个系统在信息处理方面要做什么”。

打个比方:一家餐厅的“点单—做菜—上菜”流程,可以画成一张只标注“订单从顾客到后厨、菜品从后厨到顾客”的图,而不管后厨用的是电磁炉还是燃气灶,也不管传菜是服务员走还是传送带送。这张图换一家餐厅照样成立,因为它描述的是逻辑,不是某一家店的具体做法。数据流图的价值,正来自这种“换个实现也依然成立”的稳定性。

这种稳定性带来一个直接好处:需求阶段讨论的东西,不会因为后来换了技术方案就全部作废。技术选型可能推翻重来,但“订单要经过审核才能进入处理环节”这个逻辑,通常不会变。把不变的部分单独画出来,是控制项目风险的一种朴素办法。

2.2 它是分析人员和用户之间的翻译器

数据流图有一个别的图很难替代的特点:面向用户,且不涉及技术细节。图形描述简明、清晰,即使完全不懂信息技术的业务人员,也容易理解。

这一点在实际项目里极其重要。业务人员说不清“我要一个什么样的系统”,但他们非常清楚“这张单子先给谁看、看完盖什么章、然后送到哪个柜子”。技术人员听不懂业务话术,但他们看得懂方框和箭头。数据流图正好卡在中间:业务人员看到的是自己熟悉的单据和岗位,技术人员看到的是数据的来源、去向和转换点。

于是它成了一种有效的交流手段。开会的时候,把图往桌上一摊,业务人员指着某个箭头说“这一步不对,合格的单子不会退回给客户”,问题当场就暴露了。这比对着几十页需求文档逐条念要高效得多,因为人对自己熟悉的流程有直觉,看图能立刻发现“这不是我们平时的做法”,而看文字描述则容易一路点头滑过去。

顺带一提,这也是它最大的“防翻车”价值:文字需求可以含糊,图不能含糊。箭头画不出去,就说明这件事根本没想清楚。

2.3 它还是系统设计的主要依据之一

数据流图不是画完就进档案柜的东西。它是建立系统物理模型的主要依据之一——换句话说,后面的设计工作要踩着它往前走。

道理不复杂:一张图把“有哪些数据、从哪来、被谁加工、存在哪”摆清楚了,接下来才是决定“用什么结构存、用什么模块实现、模块之间怎么调用”。前者是逻辑,后者是物理。逻辑没理顺就直接跳去写代码,最常见的后果是写到一半发现某份数据根本没人产生,或者某个加工需要的输入永远凑不齐。

从工程角度看,它至少带来三个具体收益。第一是查漏:新系统要接收哪些输入、要吐出哪些输出,一条条列在图上,漏了会很明显。第二是定界:哪些是系统该管的,哪些是门外的事,画外部项的时候就必须做决定。第三是留证据:需求评审时大家达成一致的图,日后有人提出“当初不是这么说的”,把图拿出来就能对上。

3. 四种符号各管什么:数据流、加工、数据存储、外部项

数据流图看起来简单,是因为它一共只用四种基本符号:数据流、加工、数据存储、外部项。很多人以为符号少就意味着好画,实际情况恰恰相反——符号越少,每一种符号的含义边界就越要抠清楚,否则同一张图里两个人对同一个方框的理解能差出十万八千里。

3.1 外部项:站在系统门外的那些人

外部项又称源点和终点,是系统之外的实体。它们存在于环境之中,与系统有信息交流:从源点进入系统的信息是系统的输入,从系统流向终点的信息是系统的输出。同一个端点既可以是人,也可以是其它系统。

关键在于“系统之外”这四个字。外部项不是我们开发的东西,我们管不着它内部怎么运转,只关心它跟我们的系统交换了什么。在图上引入源点和终点,目的是便于理解系统的边界,所以不需要详细描述它们,画个框写上名字就够了,必要时给个编号,以“S”开头。

举个具体的:一个报销系统,员工是外部项,财务系统也是外部项。员工在系统里点提交、看进度,财务系统在月末接收凭证数据。这两个外部项内部怎么工作,报销系统的设计者不需要知道,也不该在图上展开。

这里最容易出的问题是边界摇摆。今天把“审批领导”当外部项,明天觉得审批逻辑也该算系统的一部分,于是把它挪进来变成加工,整张图就乱了。判断标准其实很简单:这个东西是不是我们要开发的?是,就往里放;不是,就留在门外。

3.2 数据流:箭头上面那个名词

数据流由一个或一组确定的数据组成,可能是一个数据项,也可能是一个数据结构的集合。它由箭头表示,方向就是数据的流向。

关于数据流,有几条容易被忽略的规矩。第一,数据流的名字要能直观反映它的含义,写“取款单”比写“数据1”强一百倍,写“合格取款单”又比“取款单”更精确。第二,数据流可以同名,也可以有相同的数据结构,但必须对应不同的数据或者具有不同的含义——同名不同义这件事必须让人一眼看出来,否则读图的人会以为是同一份东西在到处跑。第三,两个符号之间可以同时存在多个数据流,而数据流图本身并不表明它们之间的任何关系,比如次序、主次,图上都不表达。

第三条值得多说一句。有人在两条平行箭头旁边标注“先发A再发B”,这已经超出了数据流图的能力范围,它不负责表达时序。想表达时序,得换别的图。

还有一个非常经典的血泪经验:实物不是数据流。银行的取款流程里,现金是实物,不能画成数据流;能画的是“取款单”“存折”“利息单”“结算清单”这类信息载体。初学时最容易在这里出错,画着画着就把“货物”“现金”“设备”当成箭头写上去了。

3.3 加工:把输入变成输出的那个动作

加工又称处理或变换,表示对数据流的操作。它的符号分成上下两部分:上面是标识部分,用于标注加工编号,编号要唯一,以“P”开头;下面是功能描述部分,写加工名。

加工名应当简单,能概括地说明对数据的加工行为,详细的描述放到数据字典里去定义。这一条很值得强调:图上不是写文档的地方。有人把加工框写成“根据客户等级和订单金额判断是否给予折扣并更新客户累计消费额”,这不是加工名,这是一段伪代码。规范的写法是“订单分类”或者“折扣判定”,把规则细节挪到字典里。

加工要逐层分解,目的是让分解之后的加工功能简单、易于理解。一个加工如果一句话说不清它在干什么,通常说明它还没分解到位。

3.4 数据存储:数据的停靠站

数据存储是用来存贮数据的。在分层的数据流图里,数据存储一般只属于某一层或某几层,所以又被称作局部文件。

几条硬性要求:数据存储名写在开口的长方框内,要概要地说明文件中的主要数据;数据存储上一定要有数据流,也就是说,一个孤零零没人读也没人写的存储,是画错了;为了便于说明和管理,数据存储也要编号,编号写在符号左端的小方格中,以“D”开头。

还有一个实用技巧:为了避免图上出现交叉线,同一个数据存储可以在多处画出,用重复符号表示。这不是重复定义,而是同一份数据在不同位置的“分身”,读图的人知道它们是同一个东西。

“一定要有数据流”这条规矩,背后是个很实在的道理:如果一个数据存储既没有写入也没有读出,那它对这个系统的信息处理毫无贡献,要么是设计里多余的东西,要么是某个加工漏画了箭头。两种情况都该被发现。

3.5 一张速查表,把四种符号摆在一起

符号图上的样子编号前缀一句话职责常见错误
外部项方框S系统边界外的数据来源与去处把系统内部模块画成外部项
数据流带名字的箭头F承载一组确定的数据把现金、货物等实物当数据流
加工上下两格的圆角框或圆圈P把输入数据变换成输出数据加工名写成一段规则描述
数据存储开口长方框D数据的停靠与复用画了存储却没有进出数据流

把这四种符号记住不算难,难的是随时清楚自己此刻画的是哪一类。一个常见的自查办法是:指着图上任意一个框,问自己“这是我们开发的、这是我们存数据的、还是我们外面的伙伴”,三类答案对应三种符号,答不上来就说明还没想清楚。

4. 怎么画出一张能看的数据流图:自顶向下、逐层分解

知道了符号,下一步是画。这里最怕的是“一上来就画全景”:把所有加工、所有存储、所有流向一次性堆在一张纸上,结果是一张谁也不敢看的图。规范的画法是自顶向下、逐层分解,一层只比上一层多讲一点点。

4.1 第一步到第三步:先定边界,再画关联图

绘制过程的前三步,本质上是三次收缩范围的思考。

第一步,确定所开发的系统的外部项,也就是系统的数据来源和去处。这一步定的是边界,边界定不下来,后面全是白费。

第二步,确定整个系统的输出数据流和输入数据流,把系统当成一个加工环节,画出关联图。关联图是最粗的一层:中间一个代表整个系统的加工,四周是外部项,箭头就是系统与环境之间的输入输出。这时候还不区分内部分工,只看“我们收什么、我们发什么”。

第三步,确定系统的主要信息处理功能,按此把整个系统分解成几个加工环节,也就是子系统,并确定每个加工的输出与输入数据流,以及与这些加工有关的数据存储。

拿图书预订来说:书店向顾客发放订单,顾客把填好的订单交给系统;系统先依据图书目录对订单做检查,合格订单进入处理;处理时按顾客情况和订单数量把订单分成优先订单与正常订单,优先的随时处理,正常的定期处理;最后把处理结果汇总,按出版社要求发出去。在关联图这一层,中间只有一个“图书预订系统”加工,左边是顾客,右边是出版社,箭头就是订单进、汇总订单和退回的不合格订单出。至于“检查”“分类”“统计”这些动作,此时一个都还没出现。

关联图画完,其实已经完成了一次极有价值的确认:系统跟外界交换的到底是什么。这一步错了,后面分解得再漂亮也是南辕北辙。

4.2 第四步到第五步:每一层只多讲一点

第四步,根据自顶向下、逐层分解的原则,对上层图中全部或部分加工环节进行分解。第五步,重复第四步,直到逐层分解结束。

这里的关键词是“全部或部分”。并不是每个加工都值得继续往下拆,拆到“功能简单、易于理解”就该停手。判断标准是:这个加工还需要再解释吗?如果一句话说不清,就继续拆;如果说得清,就停。

以图书预订系统的顶层图为例,它会把关联图中间那个大加工拆成三个:订单检查、订单处理、发送订单,同时在这一层逐步补充数据存储,比如图书目录、合格订单、待发出订单、出版社要求。再往下一层,“订单处理”这个加工又要拆成订单分类、数目统计、随时处理、定期处理,并新增顾客情况、订单数目、优先订单、正常订单这些存储。

于是就形成了一条链条:关联图 → 顶层图 → 二层图 → 更下层。每一层都是上一层某个加工的展开,展开时新增的箭头只出现在这个加工的内部,不能凭空冒出跟外部项直接相连的新数据流——那说明上一层的边界画漏了。

分层画还有个隐性好处:它逼着你分清主次。第一层只放最要紧的三五件事,细节留到后面。如果一张顶层图上挤了十几个加工,通常不是系统复杂,而是画的人舍不得删。

4.3 第六步到第七步:检查布局,然后拿去给用户看

第六步是检查和合理布局。主要检查分解是否恰当、彻底,各层是否有遗漏、重复、冲突之处,各层之间以及同层之间的关系是否正确,命名和编号是否确切、合理,对错误与不当之处进行修改。第七步是跟用户交流,在用户完全理解图的内容的基础上征求意见。

第七步经常被跳过,但它是整件事里最值钱的一步。数据流图的价值有一大半来自“用户看得懂”,如果画完不拿去给用户看,这个优势就浪费了。用户看得懂,才会指出“这个退回的分支我们实际不走”“这个存储我们其实是按月份分开存的”,这些都是纸面上推不出来的信息。

除了这七步,还有四条贯穿始终的原则值得记住:明确系统界面;自顶向下逐层扩展;合理布局;绘制过程就是系统逻辑模型的形成过程,必须始终与用户密切接触、详细讨论、不断修改,也要和其他系统建设者共同商讨以求一致意见。最后这条尤其现实——同一套系统,两个人各画一版,合并时对不上,返工的代价往往比一开始就坐下来对齐大得多。

4.4 用一段脚本守住“数据流必须经过加工”这条底线

绘制注意事项里有一条很硬:数据流必须通过加工。意思是数据不能从一个外部项直接飞到另一个外部项,也不能从存储直接飞到存储,中间必须经过某个加工,因为只有加工才会改变数据。这条规则靠眼睛检查很容易漏,写几行代码做个体检反而省事。

# 极简 DFD 结构体检:检查数据流是否都经过了加工 dfd = { "processes": ["P1", "P2", "P3"], # 加工编号,P 开头且唯一 "stores": ["D1", "D2"], # 数据存储,D 开头 "externals": ["顾客", "出版社"], # 外部项,S 开头(此处用名字) "flows": [ {"name": "订单", "src": "顾客", "dst": "P1"}, {"name": "合格订单", "src": "P1", "dst": "D1"}, {"name": "待发订单", "src": "D1", "dst": "P3"}, {"name": "汇总订单", "src": "P3", "dst": "出版社"}, {"name": "错误流", "src": "D2", "dst": "出版社"}, # 存储直连外部项,违规 ], } def check(dfd): problems = [] known = set(dfd["processes"]) | set(dfd["stores"]) | set(dfd["externals"]) for f in dfd["flows"]: # 端点必须存在于四类符号中,避免悬空箭头 if f["src"] not in known or f["dst"] not in known: problems.append(f"端点未定义: {f['name']}") # 规则一:源和目的不能同时是存储,数据必须先被加工 if f["src"] in dfd["stores"] and f["dst"] in dfd["stores"]: problems.append(f"存储直连存储: {f['name']}") # 规则二:源和目的不能同时是外部项,系统不能空转 if f["src"] in dfd["externals"] and f["dst"] in dfd["externals"]: problems.append(f"外部项直连外部项: {f['name']}") # 规则三:每个加工至少一进一出,否则这个加工没有意义 for p in dfd["processes"]: has_in = any(f["dst"] == p for f in dfd["flows"]) has_out = any(f["src"] == p for f in dfd["flows"]) if not (has_in and has_out): problems.append(f"加工进出不全: {p}") return problems for msg in check(dfd): print(msg)

这段代码不参与画图,只做规则校验。processes、stores、externals三个列表分别登记加工、存储、外部项的编号或名字,flows里每条记录写清数据流名和两端。函数check遍历所有数据流,先确认两端都登记过,再拦截两类违规:存储直连存储、外部项直连外部项;最后逐个加工检查是否至少有一条入流和一条出流。上面这份示例数据里,“错误流”从 D2 直接连到出版社,会被打印出来——它正是那种一眼看不出来、但会让整张图逻辑断裂的箭头。

5. 常见误区与易混概念:这五组东西别再搞混

学数据流图最费劲的地方不是画,而是分清它和旁边那些长得像的东西。下面五组是最容易混淆的,每一组都按“哪里像、为什么混、怎么分”说清楚。

5.1 数据流图与流程图:一个画名词,一个画步骤

易混点:两者都用方框加箭头,都描述“流程”,名字里还都带“flow”。

为什么混:很多教材把两者放在一起讲,加上早期一些工具把两类图形混在一个画布上,界限就模糊了。

怎么区分:看箭头上写的是什么。数据流图的箭头上永远是一个名词性短语——“取款单”“明细账”“汇总订单”,它描述的是什么东西在动。流程图的箭头上则是步骤、动作或者判断条件,还要画菱形分支、还要管先后次序。再补一句更本质的:数据流图刻意不表达次序和主次,两个符号之间可以有多个数据流同时存在;流程图的存在意义恰恰就是表达次序。想让人知道“先做什么后做什么”,那不是数据流图的活儿。

5.2 数据流与控制流:它只关心数据往哪走

易混点:都是“流”,都画成箭头。

为什么混:在真实系统里,数据流和控制信号经常一起出现,比如“处理完成后发出通知”。

怎么区分:数据流图描述的是信息的运动,不涉及“什么时候触发”“执行几次”“条件成不成立”。如果一个箭头表达的是“到这一步才开始做下一件事”,那它属于控制流。数据流图里可以画“不合格取款单”这条流,但不能画“如果不合格则跳到第几步”,因为后者是控制逻辑。图画完发现自己在纠结“这个分支怎么表示”,通常说明画错了图。

5.3 加工与程序模块:分解的是功能,不是调用关系

易混点:加工有编号、有名字、能逐层分解,看起来很像模块。

为什么混:都讲“分解”,都有层次结构,都带编号。

怎么区分:加工的分解是功能分解——把一件复杂的事拆成几件简单的事,判断依据是“这个加工还需不需要再解释”。模块的分解是调用分解——谁调用谁、谁给谁传参数。数据流图上一个加工下面挂两三个子加工,不意味着父加工“调用”了子加工,只意味着父加工描述的功能由这几个部分构成。想表达调用关系,那是结构图的地盘。

5.4 外部项与数据存储:一个在门外,一个在屋里

易混点:两者都画成方框,都跟数据流相连,看起来都是“存放东西的地方”。

为什么混:外部项的名字常常是“客户”“出版社”这种名词,存储的名字也是“图书目录”“订单库”这种名词,光看文字不好分辨。

怎么区分:问一句“这是我们系统自己维护的数据吗”。是,就是数据存储,我们负责往里写、往里读、还要给它编号(D 开头)。不是,就是外部项,它在我们管不着的地方,我们只是跟它交换数据(S 开头)。还有一条辅助判断:数据存储上一定要有数据流,且这些流通常来自或去往加工;外部项则可以只跟加工打交道。把系统内部的部门画成外部项,是边界没定清;把外部系统当存储,是把别人的地盘当自己家。

5.5 数据流图与数据字典:骨架与血肉

易混点:两者总是成对出现在需求文档里,都描述数据。

为什么混:图上写了“合格订单”这个名字,字典里也写了“合格订单”的定义,看起来是重复的。

怎么区分:数据流图负责画骨架——有哪些数据流、经过哪些加工、停在哪些存储;数据字典负责填血肉——每条数据流由哪些数据项组成、每个数据项什么类型什么取值范围。正因为加工名要求写得简单,详细描述才必须放到数据字典里去定义,两者是有分工的,不是重复劳动。只画图不写字典,图上的名字就成了空壳;只写字典不画图,一堆字段定义看不出系统怎么运转。

6. 什么时候该用、什么时候别用:拿一次取款流程走一遍

把该说的都说完,最后还是得落到“什么时候拿出来用”。我的经验是看两件事:有没有非技术的人要参与判断,以及这件事的关键是不是“信息怎么流动”。两个答案都是“是”,画图;否则换工具。

拿一次银行取款走一遍最直观。储户把填好的取款单和存折交给银行,银行审核并查对账目,不合格的退回,合格的送去取款处理;处理时修改账目,把存折、利息单、结算清单和现金交给储户,同时把取款单存档。第一步画关联图:中间一个“取款系统”,左边是储户,右边也是储户,输入是取款单和存折,输出是存折、利息单、结算清单。这里就有个坑——现金是实物,不能作为数据流,只能体现在单据里。第二步逐层分解:把“取款系统”拆成取款审核和取款处理两个加工,中间补上账目库和取款记录两个存储,不合格存折、合格存折这些流就自然长出来了。

场景适合用数据流图吗原因
需求调研,业务方在场适合业务方能看懂框和箭头,能当场纠错
需要确认系统边界适合外部项一画,边界就没法含糊
排查“某份数据没人产生”适合顺着箭头往回找,断点很明显
讲清执行顺序和分支不适合它不表达次序,该用流程图
描述模块调用与接口不适合那是结构图和接口文档的活
决定用什么数据库不适合它刻意不涉及文件组织与数据结构

不适用的地方也别硬用。有人把数据流图当项目进度图,在箭头上标工期;有人拿它当数据库设计图,在存储里写字段类型。这两件事都会让图迅速失去可读性,最后谁都不看。

从那以后我每次动手画之前,都先强制走一遍最笨的流程:把外部项的名字一个个写在纸上,把系统跟每个外部项之间的输入输出列清楚,再决定里面怎么拆。这一步花不了二十分钟,却能省掉后面反复改图的时间。画图这事儿没什么玄学,边界定住了,剩下的都是耐心活。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进