Day25-01|5.3.1 结构化方法:用数据流和功能分解把复杂业务变成可设计对象

本文对应《系统架构设计师教程(第2版)》第5章“软件工程基础知识”5.3.1“结构化方法”。结构化方法是一类以功能分解、数据流分析和模块化设计为主要特征的软件开发方法。它通过自顶向下逐层分解系统,把业务处理、数据输入输出和控制关系表达成较清晰的模型。虽然现代项目大量采用面向对象、服务化和领域驱动方法,但结构化方法中的分解、抽象、数据流和模块设计仍是系统架构师分析遗留系统、梳理批处理流程和进行综合建模的重要基础。

一、学习目标与考试定位

完成本节后,应能够解释结构化方法的基本思想、开发过程和主要模型;掌握数据流图、数据字典、加工说明、决策表、决策树和实体关系模型在结构化分析中的作用;理解变换分析和事务分析如何指导结构化设计;能够进行模块划分、接口设计和模块独立性分析;能够比较结构化方法与面向对象方法的差异;能够在案例题和论文中使用结构化方法解决业务流程和数据处理问题。

软考题目常给出一段业务描述,要求画数据流图、补充数据字典、识别外部实体、数据存储和处理,或者根据数据流完成模块分解。答题时要区分数据流、数据存储、处理和外部实体,不能把流程图、组织结构图和数据流图混用;模块划分还要关注高内聚、低耦合、接口清晰和信息隐藏。

二、结构化方法的基本思想

结构化方法采用自顶向下、逐步求精的方式,把一个复杂系统分解为若干层次的功能和处理。先理解系统目标和边界,再识别主要业务处理、数据流和外部交互,随后逐层细化,直到每个处理可以由清晰的程序模块实现。结构化方法强调模型化、文档化和规范化,适合需求相对稳定、业务流程和数据处理边界清晰的系统。

它通常包含结构化分析、结构化设计和结构化程序设计三个相互联系的部分。结构化分析关注“系统要做什么”,用数据流和业务规则表达需求;结构化设计关注“系统如何组织”,将分析模型转化为模块结构和接口;结构化程序设计关注“模块如何实现”,强调顺序、选择和循环等基本控制结构,减少无条件跳转和难以理解的控制流。

核心思路可以概括为:

明确边界 → 识别数据流 → 分解处理 → 描述数据和规则 → 组织模块 → 验证接口与质量

三、结构化分析的主要模型

1. 数据流图

数据流图(Data Flow Diagram,DFD)描述数据在系统中的流动、变换、存储和外部交互,关注数据“从哪里来、经过什么处理、到哪里去”,不强调程序执行的时间顺序。基本元素包括外部实体、处理、数据流和数据存储。

外部实体表示系统边界外与系统交换数据的人、组织或其他系统;处理表示对输入数据进行变换的功能;数据流表示有方向的数据移动;数据存储表示需要保存和读取的数据集合。DFD 要有明确的输入输出和层次关系,不能出现只有输入没有输出、只有输出没有输入的黑洞或奇迹处理,也不能直接把数据流连接到数据流而没有处理。

2. 数据字典

数据字典定义数据流、数据存储、数据项和处理相关的结构、含义、类型、来源、去向和约束。它解决模型中的术语歧义,例如“客户信息”到底包含哪些字段,“订单金额”是否包括税费和折扣,“审批结果”有哪些取值。数据字典是数据流图的补充,也是数据库设计、接口设计和测试的重要输入。

3. 加工说明

加工说明描述某个处理如何根据输入产生输出,可以使用结构化语言、判定表、判定树、伪代码或数学表达式。对规则复杂、条件组合多的业务,决策表比自然语言更容易发现遗漏和冲突;对分支层次明显的业务,决策树更直观。

4. 实体关系模型

实体关系模型用于描述业务实体、属性和联系,帮助分析数据结构和数据约束。结构化分析通常同时关注数据流和数据结构:DFD 表达数据如何流动,ER 模型表达数据由什么组成、如何关联。二者结合可以减少“流程分析完成但数据设计不清”的问题。

DFD 元素 含义 示例
外部实体 系统外部的交互主体 客户、银行、监管系统
处理 变换输入为输出的功能 校验订单、计算费用
数据流 数据移动及方向 订单请求、审批结果
数据存储 持久化数据集合 订单库、客户档案

四、数据流图的层次化建模

顶层图通常把整个系统表示为一个处理,展示系统与外部实体之间的主要输入输出,也称上下文图。下一层将系统分解为若干主要处理、数据存储和内部数据流。继续分解时,要保持父图和子图的输入输出平衡:子图向外暴露的数据流应与父处理一致,不能无缘无故新增或消失。

层次化的价值是控制复杂度。顶层帮助利益相关者理解边界,中层展示主要业务能力,底层描述具体处理和数据规则。分解到什么程度要以可理解、可验证和可实现为准,不是越深越好。若一个处理包含多个独立业务规则、多个角色和多个数据存储,就可能需要继续分解;若继续分解后模型比代码还难读,应考虑停止。

常见 DFD 错误包括:外部实体直接连数据存储,未经过处理;处理只有输入没有输出;数据存储之间直接传递数据却没有处理;父子图不平衡;把控制信号或时间顺序误画成普通数据流;同一数据流名称在不同图中含义不一致。评审时应结合数据字典和业务场景逐项检查。

五、结构化分析的基本步骤

第一步确定系统目标、范围、外部实体和主要业务事件;第二步绘制上下文图,确认输入输出和系统边界;第三步建立顶层 DFD,识别主要处理和数据存储;第四步逐层分解,补充数据字典和处理说明;第五步分析业务规则、例外和非功能要求;第六步建立实体关系模型和数据约束;第七步通过走查、原型和场景验证模型;第八步把确认后的分析模型交给结构化设计。

分析过程中要不断区分“业务事实”和“实现方案”。例如“申请经过审核后产生结果”是业务需求,“使用消息队列实现审核通知”是技术设计;“保存历史记录”是数据和审计要求,“使用某数据库”是实现选择。过早把技术方案写进分析模型,可能限制后续架构选择。

六、结构化设计:从处理到模块

结构化设计把分析得到的处理和数据流转换成模块层次结构。模块是具有明确职责、接口和内部实现的设计单元。设计目标包括合理分解、控制模块规模、提高内聚、降低耦合、隔离变化和便于测试维护。

对于数据流结构,常见设计思路是变换分析:识别输入流、中心变换和输出流,将输入、变换和输出分别组织成模块;对于存在多个事务类型的系统,可以采用事务分析:先识别事务中心,根据事务类型选择不同处理分支。实际项目可以组合使用,重点是让控制和数据依赖清晰。

模块独立性通常通过内聚和耦合评价。内聚表示一个模块内部职责关联程度,功能内聚较好;耦合表示模块之间依赖程度,数据耦合通常优于公共耦合和内容耦合。模块不应直接修改另一个模块的内部数据,应通过明确接口传递必要信息。全局变量、隐式共享状态和循环调用会降低可维护性。

七、结构化程序设计

结构化程序设计使用顺序、选择和循环三种基本控制结构组织程序,要求控制流清晰、入口和出口明确、嵌套层次适度。它反对依赖大量无条件跳转形成的“面条式代码”,因为这种代码难以理解、测试和证明正确。

结构化程序设计还强调逐步求精:先描述模块的主要步骤,再逐步细化为可执行逻辑;使用伪代码和流程图表达算法;对异常、边界和错误处理进行明确设计。它与面向对象方法并不完全冲突,现代对象内部的服务方法同样需要结构化控制流。

八、结构化方法与面向对象方法比较

结构化方法以功能和数据流为主要分析线索,把系统看作输入经过处理产生输出;面向对象方法以对象、类、责任、协作和状态为主要线索,把数据和行为封装在对象中。结构化方法适合流程稳定、批处理、数据转换和过程导向的场景;面向对象适合业务对象复杂、状态丰富、变化频繁和需要复用扩展的场景。

结构化方法的优点是过程清晰、模型直观、便于分层和文档化;缺点是数据与处理可能分离,需求变化时功能分解结构容易受到影响。面向对象方法有利于封装、复用和演化,但需要较强的抽象、建模和设计能力。现实系统常混合使用:用数据流梳理跨系统流程,用对象和领域模型设计核心业务,用服务和接口组织部署边界。

九、案例分析:批量理赔系统的结构化分析

某保险公司需要建设批量理赔系统。外部实体包括客户服务系统、支付系统和监管系统;输入包括理赔申请、保单信息和材料清单;系统要完成资料校验、责任判断、金额计算、人工复核、支付指令和监管报送。

分析时先绘制上下文图,明确申请、校验结果、支付指令和报送数据的边界;再分解为申请接收、资料校验、保单匹配、责任判断、金额计算、复核处理、支付和报送等处理;建立理赔申请、保单、规则、复核记录和支付记录的数据存储;用数据字典定义金额、状态、材料和错误码;用决策表描述责任判断和金额计算规则;用 ER 模型明确申请与保单、材料、复核和支付的关系。

结构化设计可以将输入接收、规则处理、输出和异常处理分成模块,并通过统一的理赔状态和接口模型协作。对批量文件采用异步处理和断点续跑,对支付和报送采用幂等和审计,对规则高频变化部分进行配置化隔离。虽然分析方法结构化,但实现可以使用面向对象、服务化和消息驱动技术。

十、常见误区

第一,数据流图就是流程图。DFD 重点是数据的来源、处理、存储和去向,不强调时间顺序。第二,结构化方法只适合老系统。其分解、接口、数据字典和规则表达仍适用于现代系统。第三,分解层次越多越好。模型应保持可理解和可验证。第四,模块划分只按页面或数据库表。模块应围绕职责、内聚、变化和接口划分。第五,结构化方法与面向对象完全互斥。两者可以按问题空间组合使用。

十一、课后练习

练习一:单项选择

数据流图中表示数据持久化集合的是( )。

A. 外部实体;B. 处理;C. 数据流;D. 数据存储。

答案:D。数据存储表示系统需要保存和读取的数据集合。

练习二:简答题

请说明数据流图分层分解时为什么要求父图和子图平衡。

参考答案:父图表示较高层处理与外部交互,子图是对该处理的细化。保持输入输出平衡可以确保分解没有无依据地增加、遗漏或改变系统边界,维持模型的一致性和可追踪性。子图内部可以增加处理和数据存储,但对外暴露的数据流应与父处理的输入输出一致,并通过数据字典统一含义。

练习三:案例分析

某项目用三个独立模块分别处理订单录入、订单查询和订单统计,但三个模块都直接读写同一组全局订单变量,修改一个字段会影响全部模块。请分析设计问题并提出改进措施。

参考答案:三个模块对公共数据存在强耦合和隐式依赖,属于公共耦合倾向;数据结构变化会传播到所有模块,难以测试、维护和并行开发。应按订单管理职责封装数据和操作,提供清晰的数据接口或服务接口;查询、统计和录入通过契约访问订单能力,禁止直接修改内部数据;必要时使用领域服务或数据访问层隔离存储;为接口、并发、权限和事务建立测试;对公共字段变更进行影响分析和版本兼容,逐步消除全局状态。

十二、论文素材

论文可写:在遗留业务流程复杂、数据口径不一致的项目中,我先采用结构化分析明确系统边界和外部实体,绘制上下文图和分层数据流图,配合数据字典、处理说明、决策表和实体关系模型澄清业务规则、数据来源与去向;随后以高内聚、低耦合和信息隐藏为原则进行模块划分,通过清晰接口隔离变化和共享数据。对于批处理、规则计算和跨系统数据交换采用结构化方法梳理,对于复杂业务对象再结合面向对象和服务化设计,并通过模型走查、接口测试和需求追踪验证分析结果。

十三、今日复盘

结构化方法以功能分解、数据流和模块化为核心,通常经历结构化分析、结构化设计和结构化程序设计。数据流图表达数据流动,数据字典解释数据结构,处理说明和决策表表达规则,实体关系模型表达数据关系;分层建模要保持父子图平衡,模块设计要关注内聚、耦合、接口和信息隐藏。结构化方法与面向对象并非互斥,可按问题特征组合使用。下一篇进入 5.3.2 面向对象方法。