数据流图DFD与需求分析:图书馆管理系统建模实践 简介这份PPT面向软件工程课程学习者与讲授需求分析建模的教师围绕面向数据流的分析方法展开借助小型图书馆管理系统案例梳理从现实环境到计算机世界的映射过程并说明需求分析各阶段的衔接关系。内容覆盖抽象与模型、当前系统物理模型与逻辑模型的抽取、目标系统逻辑模型的建立、细化与验证以及需求定义、功能性/非功能性需求、需求获取和性能约束等要点还涉及数据领域、功能领域与行为领域的描述并对结构化开发方法、面向对象开发方法与需求分析建模方法作了对照说明。压缩包仅含1个PPT文件约1.41MB篇幅集中便于课堂演示或自学复盘。已有226人学习适合软件工程需求分析章节的预习、复习与案例讨论也可作为理解数据流分析与逻辑模型构建的辅助材料。1. 面向数据流的分析方法为什么需求不是“把功能列出来”把需求写成功能清单看起来最省事却往往是后面返工率最高的起点。面向数据流的分析方法不急着设计类也不先画界面而是把现实世界里的借书、还书、购书、检索还原成一张可追踪的数据流动图谁发出请求、数据在哪个处理节点变形、落到哪个数据存储、最后回流给谁。这套课件真正有价值的地方不是讲几个 DFD 符号而是用一个小型图书馆管理系统把“物理模型→逻辑模型→目标逻辑模型→细化与验证”的链路走了一遍。读者要搞懂的是为什么图能替代大段文字以及一张图怎样被检查出不完整、不一致和不可实现。2. 数据流图不是流程图四个元素如何把现实压成可验证模型数据流图最容易被误读成“画流程”。流程图关心先做哪一步、后做哪一步数据流图关心数据从哪里来、经过什么变换、被存到哪里、又流向谁。这个差别决定了需求分析阶段能不能把“业务上必须成立的约束”从“当前人工流程的习惯”里剥离出来。课件里教材购买的案例很典型现实中的部门、纸单据、人工传递路径都是物理模型的一部分去掉与目标系统无关的部门名和单据载体之后剩下“审查有效性、开发票、开领书单、发书”这些数据变换才是逻辑模型。2.1 从购书流程到逻辑模型抽象到底删掉了什么课件把需求分析过程拆成几步调查当前环境得到当前系统的物理模型去掉非本质因素抽象出当前系统的逻辑模型比较当前系统与目标系统的差别建立目标系统的逻辑模型再对目标逻辑模型细化、改进和验证。这里的“抽象”不是简单删细节而是删掉与目标问题无关的实现载体保留业务上必须成立的数据变换和约束。以教材购买为例物理模型里会出现学生、教务部门、财务、教材管理岗位以及购书申请、购书单、发票、领书单这些纸质载体。逻辑模型不关心“谁把单子送到哪个办公室”它关心的是购书申请要经过有效性审查审查通过后产生发票和领书单最后发书无效申请要有明确的回流路径。到了目标系统逻辑模型计算机接管的是这些数据变换而不是把原来的部门墙原样搬进软件。这也是“现实世界→计算机世界”的映射难点。物理模型回答“现在是怎么运转的”逻辑模型回答“业务上必须完成哪些信息变换”目标逻辑模型回答“哪些变换要由计算机系统承担”。三者的边界一旦混在一起DFD 就会变成一张既像组织架构图、又像程序流程图的四不像。常见的实现思路是先把物理模型中的人、部门、纸张、传递方式标出来再逐项问“删掉它之后业务语义是否仍然完整”。如果删掉后约束丢了就不能删如果删掉后只是实现方式变了就应该抽象掉。2.2 DFD 四个符号的语义边界外部实体、加工、数据存储、数据流数据流图的基本元素不多但每个元素都有明确边界。外部实体是系统边界之外的交互对象它不负责系统内部实现加工是对数据做变换的处理单元必须有输入和输出允许有异常输出数据存储是数据静止的位置是逻辑存储不等同于某张数据库表数据流是带有名称和内容的数据包不是控制信号也不是函数调用。元素回答的问题建模边界常见误用外部实体谁或哪个系统与系统交换数据在系统外部不展开内部实现把内部部门画成外部实体导致边界虚胖加工数据被怎样变换必须有输入和输出只有输入没有输出形成“黑洞加工”数据存储数据静止在哪里逻辑存储不是物理表或文件把数据存储当成数据库设计图数据流数据沿哪条路径移动有名称、有内容、有方向画成执行顺序或控制流这里最需要拧清的是“数据流不是控制流”。借书请求从读者流向系统表示读者把借书信息交给系统不表示“系统先执行读者模块再执行借书模块”。DFD 的箭头表达的是数据可用性依赖不是时间顺序。很多评审稿把“先校验、再更新、再打印”画成一串箭头实际上那已经是加工说明或时序图的内容不应该塞进 DFD 里冒充数据流。另一个边界是“加工必须可分解但不能无限分解”。顶层加工可以继续拆成子加工外部实体一般不再展开数据存储可以在父图出现、在子图细化也可以只在子图局部出现。判断标准不是图好不好看而是每个加工的输出能不能被下游消费每个数据存储的字段能不能被某个加工读写每个外部实体的输入输出是否真的跨越了系统边界。2.3 分层 DFD 的父子平衡0 层、1 层、2 层怎么对齐数据流图通常分层画上下文图把整个系统当成一个加工只保留外部实体和系统边界0 层图把系统拆成若干主要加工1 层图再对某个主要加工继续分解。分层不是为了多画几张图而是为了把复杂度压到每一层都能被人评审。父子平衡是分层 DFD 的硬约束父图中某个加工的输入输出数据流必须在子图边界上出现父图的数据存储可以在子图中细化但不能凭空消失或凭空出现。举例来说上下文图里读者向图书馆系统发出“借书请求”系统返回“借书结果”到了 0 层图借书请求可能被拆成“读者身份”“图书副本标识”“借阅时间”等数据流但“借书结果”仍然必须作为系统输出出现。如果 0 层图只画了内部的状态更新没有把结果流回读者父子图就不平衡。再往下拆“借书”加工时子图可以出现“借阅记录”“副本状态”这些内部数据流但父图里“借书请求”和“借书结果”这两个边界流不能丢。父子平衡检查不能只看名字。父图叫“借书请求”子图叫“借阅参数”名字不同但内容一致可以通过数据字典对齐反过来名字一样但组成不同仍然是断链。实际评审时我会要求每个边界流都能在数据字典里找到组成每个子图输出都能回答“父图对应的输出在哪里”。这条检查很笨但能挡住大量“图很漂亮、逻辑接不上”的稿子。3. 图书馆管理系统五类功能怎样落到可检查的数据流模型课件给出的图书馆管理系统功能不多但足够暴露需求分析里的典型问题借书、还书增加或删除一本书按作者名或专业领域检索一批书找出某位读者借出的一批书找出最近借走某本图书的读者。系统有两类用户图书管理员和普通读者。功能 4 供普通读者使用功能 1、2、5 供图书管理员使用。约束也很明确馆中所有未借出的书籍能够供读者随时借阅同一时刻一本书不能既被借出又被借阅一个读者一次借出的书籍数目不能超过预定值。真正要做的是把这些功能、角色和约束拆到 DFD 的层级、数据存储和加工说明里。3.1 上下文图与事件划分两类用户、五个功能边界上下文图先不急着拆功能而是确定系统边界。图书馆管理系统的外部实体至少包括普通读者和图书管理员系统输入包括借书请求、还书请求、图书增删信息、检索条件、读者借阅查询条件、图书最近借阅者查询条件系统输出包括借阅结果、归还结果、检索结果、借阅清单、最近借阅者信息以及各类失败原因。功能主要触发者输入输出建模注意点借书、还书图书管理员读者标识、图书副本标识借阅或归还结果要区分书目与馆藏副本增加、删除图书图书管理员书目信息、副本信息维护结果删除要考虑历史借阅记录按作者或领域检索课件未写死作者名或专业领域一批书目需确认普通读者和管理员是否都可触发找出某读者借出的书普通读者读者标识未归还图书清单只查当前借出还是含历史借阅找出最近借走某书的读者图书管理员图书标识最近借阅者需要借阅时间与副本标识功能 3 的触发者在课件里没有写死这不是缺陷而是需求分析必须追问的开放问题。检索由普通读者发起、由管理员代查还是两者都可以会改变外部实体、权限边界和界面入口。事件划分的思路是把每个外部事件当成一个触发点借书事件、还书事件、图书维护事件、检索事件、读者借阅查询事件、图书最近借阅者查询事件各自对应一组输入输出。事件划分清楚之后DFD 的 0 层图才不会变成功能清单的堆叠。3.2 一级分解借还、图书维护、检索、读者借阅查询、图书借阅追踪0 层图可以围绕五类功能拆成五个主要加工借还书处理、图书维护、书目检索、读者借阅查询、图书借阅追踪。这里最关键的数据存储不是“图书”一个笼统概念而是书目与馆藏副本的分离。书目记录题名、作者、专业领域等描述信息馆藏副本记录每一本可被借阅的物理或逻辑副本以及它的可借状态。没有副本标识借阅记录就无法准确表达“谁借走了哪一本”也无法支撑“同一时刻一本书不能既被借出又被借阅”的约束。数据存储标识关键字段为什么需要书目书目标识题名、作者、专业领域支撑按作者或领域检索馆藏副本副本标识书目标识、状态、馆藏位置支撑借出、归还和互斥状态读者读者标识读者类型、借阅上限支撑借阅资格和数量限制借阅记录记录标识副本标识、读者标识、借出时间、归还时间支撑读者借出清单和最近借阅者查询加工与数据存储的读写关系可以进一步摊开借书加工读读者、读馆藏副本、读借阅记录计数写馆藏副本状态、写借阅记录还书加工读未归还的借阅记录写馆藏副本状态、写归还时间图书维护加工写书目和馆藏副本但删除时要检查是否存在未归还记录书目检索只读书目读者借阅查询读借阅记录、馆藏副本和书目图书借阅追踪读借阅记录、读者和馆藏副本并按借出时间倒序取最近一条。这些读写关系看起来像数据库操作但它们在需求阶段的作用不是提前做表设计而是验证 DFD 是否闭合。一个加工如果只写不读或者一个数据存储只被一个加工写、从来没有加工读就要追问它是不是多余的。检索加工只读书目说明它不应该承担借阅状态判断借阅查询要读借阅记录和副本说明它和借书加工共享同一套数据语义。数据流图不负责写 SQL但它必须让后续的表结构有可追踪的来源。3.3 约束如何写进模型互斥借出、可借状态和借阅上限DFD 本身不擅长表达“同一时刻”和“状态互斥”。它能看到数据流却看不到两个请求并发到达时谁先谁后。图书馆案例里的三条约束必须用状态、数据字典和加工说明补上不能指望一张 DFD 自动证明。第一“馆中所有未借出的书籍能够供读者随时借阅”是一个不变量对每个馆藏副本如果没有未归还的借阅记录它的状态就应该是可借如果有未归还记录它的状态就应该是已借出。第二“同一时刻一本书不能既被借出又被借阅”要求借书加工在检查可借状态和写入借阅记录之间不能被另一个请求插入。第三“一个读者一次借出的书籍数目不能超过预定值”要求借书加工在写入前读取该读者未归还记录数并与读者类型对应的上限比较。状态事件前置条件后置条件可借借出读者存在、未超上限、副本可借副本变为已借出新增未归还记录已借出归还存在该副本的未归还记录副本变为可借记录写入归还时间可借删除无未归还记录副本退役或逻辑删除已借出删除不允许直接删除先归还再退役加工说明要把这些前置条件和后置条件写清楚借书加工的前置是读者存在、副本状态为可借、该读者未归还数量小于上限后置是副本状态变为已借出并新增一条未归还借阅记录异常包括读者不存在、超限、副本不可借。还书加工的前置是存在该副本的未归还记录后置是副本恢复可借并写入归还时间。这样DFD 负责展示数据流向状态表负责展示生命周期数据字典负责统一术语三者合起来才是可检查的需求模型。需要提醒的是借阅上限不能只写在界面校验里。界面校验是体验层模型层的约束必须落到借书加工的事务边界内否则两个并发请求都看到“未超限”就可能同时借出把上限撑破。常见实现思路是用条件更新、行锁或唯一约束把“检查”和“写入”放进同一个原子操作里需求分析阶段不写具体技术但必须把“原子性”作为约束写进加工说明否则设计阶段很容易漏掉。4. 从 DFD 到模块结构变换分析和事务分析到底在切什么DFD 画到一定程度下一步不是继续堆图而是把数据流视图转成职责视图。结构化分析方法里常用变换分析和事务分析变换分析找“输入流—变换中心—输出流”事务分析找“请求分派中心”。这两种分析不是数据库事务也不是把 DFD 直接翻译成代码而是帮助判断模块应该怎么切、控制模块应该放在哪里、哪些加工适合独立封装。4.1 变换分析输入流、变换流、输出流怎么切成模块借书和还书是典型的变换型处理。以借书为例输入流是读者标识和图书副本标识逻辑输入是经过格式检查和存在性检查后的借阅参数变换中心是“判断能否借出并更新副本状态与借阅记录”逻辑输出是借阅结果输出流是把结果格式化成管理员或读者能看到的信息。变换分析会把这个过程切成输入校验模块、借阅变换模块、结果输出模块再加一个顶层控制模块负责调度。模块职责输入输出借阅输入校验校验读者、副本、参数完整性读者标识、副本标识合法借阅参数或错误借阅变换检查上限、更新状态、写借阅记录合法借阅参数借阅成功或失败原因借阅输出组织结果信息借阅结果借阅回执或错误提示借阅控制调度上述模块外部请求统一响应这种切法的好处是输入校验和业务变换解耦借书与还书可以共享部分输入输出骨架但变换中心不同。借书的变换中心是“占用副本并生成借阅记录”还书的变换中心是“释放副本并关闭借阅记录”。如果把它们硬塞进一个“借还书大模块”输入输出会纠缠异常路径也会混在一起。变换分析的关键不是画模块结构图而是找对变换中心。找错了模块会切得很碎控制模块变成只会转发参数的壳找对了每个模块的职责都能用一句话说清耦合也会降下来。实际评审时我会问这个模块的输出是不是下一个模块的输入如果它只是把数据存储原样透传那它可能不该独立成模块。4.2 事务分析读者请求的分支中心检索、读者借阅查询、图书最近借阅者查询更像事务型处理系统先接收一个请求再根据请求类型分派到不同处理分支。按作者名检索、按专业领域检索、查某读者借出的书、查某本书最近借阅者处理逻辑不同但入口都可以收敛到一个“请求分派”中心。事务分析就是围绕这个分派中心切模块顶层负责识别请求类型下层每个事务分支负责自己的数据读取和结果组织。维度变换分析事务分析中心主变换加工请求分派中心适合输入—变换—输出较线性多分支请求、查询或维护入口切分依据数据变换阶段事务类型常见风险把查询也硬画成变换流分派粒度过细模块数量失控事务分析容易踩的坑是把“分派”做成大而全的 switch把每个分支的逻辑都塞进分派模块。更稳的做法是分派模块只负责识别请求和路由每个事务分支独立封装查询条件、数据读取和结果组装。这样新增一种检索方式时不需要改所有分支只需要增加一个事务模块并注册到分派入口。4.3 数据字典与加工说明让图能“跑”起来DFD 只画数据流不定义数据内容所以必须有数据字典。数据字典要把每个数据流、数据存储、数据项的名称、组成、别名、取值范围和约束写清楚。没有数据字典图上的“借书请求”可能一个人理解成读者标识加副本标识另一个人理解成书名加读者姓名评审时看似点头设计时立刻打架。条目组成约束借书请求读者标识 副本标识两者均存在副本可借书目书目标识 题名 作者 专业领域书目标识唯一馆藏副本副本标识 书目标识 状态状态属于可借、已借出、退役借阅记录记录标识 副本标识 读者标识 借出时间 归还时间归还时间为空表示未归还借阅上限读者类型 上限值上限为正整数加工说明则负责打开“黑匣子”。每个加工至少要有输入、输出、前置条件、后置条件和异常路径。借书加工的后置条件不能只写“借书成功”而要写“副本状态变为已借出新增一条未归还借阅记录”还书加工的后置条件要写“副本恢复可借借阅记录写入归还时间”。这些说明不是实现细节而是需求模型可验证的最小单位。数据字典和加工说明还能反向检查 DFD如果某个数据流在字典里找不到组成说明图上有悬空名称如果某个数据存储的字段没有任何加工读写说明它可能是冗余存储如果某个加工只有正常输出没有异常输出说明异常路径还没建模。结构化分析的价值就在这里它不保证需求一定正确但它能把不一致暴露在评审桌上。5. 需求分析建模的避坑与排查五条血泪经验数据流分析看起来符号简单实际评审时最容易翻车的不是“不会画”而是“画了但接不上”。下面五条是图书馆案例里最容易出现的坑每条都按现象、原因、解决来拆。5.1 图层面的坑父子图不平衡与“黑洞加工”现象父图里明明有“借书结果”输出子图里却只有内部状态更新没有结果流回外部实体。评审时顺着父图找不到子图对应输出只能靠口头解释补。原因通常是分解时只盯着内部处理忘了子图边界必须继承父图加工的输入输出。解决是建立父子平衡检查表逐条列出父图加工的输入流和输出流再到子图边界上找同名或同内容的数据流名字不同就用数据字典对齐内容不同就补流或改父图。现象某个加工只有输入没有输出或者只有输出没有输入图上看起来像个黑洞。原因往往是把控制命令当成了数据流或者漏掉了异常输出。解决是给每个加工写输入输出清单正常路径和异常路径都要有数据流控制信号不画进 DFD执行顺序放到加工说明或时序描述里。现象把数据流箭头画成“先校验、再更新、再打印”的执行顺序。原因是把 DFD 当成了流程图。解决是回到数据视角箭头两端必须是数据生产者与消费者箭头上写的是数据内容不是动作步骤如果两个加工之间没有数据传递只是调用关系就不应该用数据流连接。5.2 数据与约束层面的坑一本书的歧义、借阅上限与删除现象借阅记录里只写“书目标识”两个读者同时借同一题名的不同副本时系统无法判断到底哪一本被借走。原因是把“书”当成书目没有区分书目与馆藏副本。解决是拆出书目和馆藏副本两个数据存储借阅记录绑定副本标识检索面向书目借还面向副本最近借阅者查询也按副本或明确的书目口径定义。现象借阅上限在界面上校验通过但并发请求下仍然超限。原因是“检查未归还数量”和“写入借阅记录”不在同一个原子边界内两个请求都读到了旧计数。解决是在借书加工说明里写明事务约束设计阶段用条件更新、行锁或唯一约束保证检查与写入的原子性需求评审时要追问“同一读者并发借书”这个场景而不是只测单用户点击。现象图书维护里直接物理删除书目历史借阅记录找不到对应图书功能 5 查最近借阅者时出现断链。原因是需求只写了“增加/删除一本书”没有定义删除是删书目、删副本还是退役也没有规定历史记录保留策略。解决是把删除拆成“书目停用”和“副本退役”存在未归还记录的副本不允许删除借阅记录保留副本标识和书目快照至少保证历史查询可解释。现象读者借阅查询把已归还记录也混进“当前借出”清单。原因是查询条件只按读者标识过滤没有限定归还时间为空。解决是明确功能 4 的口径到底查当前未归还还是查全部历史如果是当前借出就在加工说明里写清“归还时间为空”的前置过滤并在数据字典里定义“未归还记录”的语义。6. 设计取舍数据流分析的价值不在图而在可追踪的约束面向数据流的分析方法最大的优势是把“信息怎么变形”从“程序怎么执行”里剥离出来。它适合需求早期用户只会说借书、还书、检索开发者还没法决定类和接口DFD 能先把边界、数据存储和变换摊平。代价也明显它不擅长表达时序、并发和状态所以图书馆案例里“同一时刻不能既借出又可借”“借阅上限”必须靠状态图、数据字典和事务约束补上。我的判断是DFD 不该被当成一份要交的文档而该被当成评审时的追问工具每个加工有没有输入输出每个数据流能不能在字典里闭合每条约束有没有落到可检查的前置条件。验证时我会做三件事父子图逐流平衡数据字典逐项闭合状态不变量逐条走查。这套课件真正值得对着练的不是符号而是它把物理模型、逻辑模型、目标逻辑模型和图书馆案例串成了一条可验证的链路。从那以后我每次评审需求模型都强制先走一遍平衡、闭合、不变量这三步希望帮到你。需要原 PPT 的读者可从资源简介处取用。本文还有配套的精品资源点击获取