UML网上订餐系统建模文档:从需求模型到顺序图全链路拆解
简介:这份资源是《UML网上订餐系统分析与设计》实验报告文档,面向软件工程课程学习者、UML建模入门者以及需要完成建模大作业的高校学生,帮助读者理解如何用统一建模语言完整表达一个订餐系统的需求与设计。压缩包共1个文件,为149KB的doc文档,正文以文字说明配合图形化模型展开,便于直接阅读或改写为课程报告。内容围绕需求模型、分析模型与用例实现三部分:需求模型梳理游客、注册用户、管理员的权限划分及餐品管理、留言板、公告栏等业务;分析模型给出客户端、服务器、数据库与打印机的架构划分,并对orderlist、order、dish、user、guest、favorite等类标注持久性、安全性、通信等分析机制;关键抽象部分用多重关联呈现实体间关系,用例实现则细化注册、登录注销、餐品检索的事件流、前置与后置条件,并配有类图与顺序图。已有4230人学习,适合需要参考完整建模流程与实验报告结构的读者。
1. 一份 UML 网上订餐系统建模文档,救的是答辩现场那二十分钟
带过几轮课程设计之后我发现一个规律:最后交上来一份 UML 网上订餐系统.doc 的组,被问住的地方几乎永远在同一处——用例描述里写的事件流,跟顺序图里画的调用链对不上。评委只要顺着"注册"这条线追一遍,从事件流第 4 步追到顺序图第 5 条消息,中间断在哪就露馅了。这份实验报告真正值钱的地方不在于画了多少张图,而在于它把需求模型(角色、用例、事件流、前后置条件)、分析模型(架构、分析机制、关键抽象类)、用例实现(12 个用例的文字描述配顺序图)串成了一条能追溯的链。正在赶建模大作业的、需要给存量项目补一份可评审设计文档的,都能直接照着走一遍。下面按骨架、血肉、排雷的顺序拆开看。
2. 需求模型怎么立骨架:三个角色、十二个用例的边界切法
需求模型这一层最容易糊弄,也最容易被追问。画一张椭圆图谁都会,难的是每个椭圆背后有没有一条写清楚的事件流撑着,以及角色和用例之间的连线到底该不该存在。
2.1 角色切分:游客、注册用户、管理员的责任边界
这份报告把参与者切成了游客、注册用户、管理员三类,管理员的特殊之处在于——不提供注册入口,账号只能在数据库里追加。这一条看起来是个实现细节,其实是一个建模决策,它直接决定了"管理员管理"这个用例的参与者只有管理员本人,而且登录入口要和前台登录分开。
顺着这个思路往下推,有几个连线的取舍就清楚了:
| 参与者 | 可发起用例 | 不该连的用例 | 判断依据 |
|---|---|---|---|
| 游客 | 注册、餐品信息检索、查看评论、部分留言浏览 | 收藏夹管理、餐品选购、订单管理 | 收藏夹用例的备选流明确写了"未登录的游客不会拥有收藏夹功能" |
| 注册用户 | 登录/注销、信息管理、选购、订单、评论、留言、收藏 | 餐品管理、权限管理、公告发布 | 后台操作"其他人无权管理" |
| 管理员 | 后台登录、餐品管理、订单管理、权限管理、公告管理、留言回复 | 注册 | 账号只能在数据库中添加 |
这里有个新手常犯的错:把"浏览餐品"和"检索餐品"画成两个用例。报告里餐品信息检索的基本流第 1 步是"用户选择浏览餐品目录,或者输入查看的餐品检索信息",两条入口汇到同一个用例,说明它们在语义上是同一件事的两种触发方式。合并成一个用例、在事件流里写清两条入口,比画两个椭圆更经得起追问。
还有个更隐蔽的边界问题:留言板。基本流里管理员可以查看、回复、删除,普通用户可以添加和查看,游客只能浏览部分留言。这三者不是三个用例,而是一个用例配三条不同的权限分支。如果拆成"用户留言"和"管理员留言管理"两个用例,那么"删除恶意留言"这个动作归谁、回复动作归谁,就会在两张图里重复描述,评审时必然被抓。
2.2 用例描述六件套:简要说明、事件流、特殊要求、前置条件、后置条件、扩展点
报告里每个用例都按这六项填,格式是统一的。这套模板本身没什么稀奇,值钱的是填写时的颗粒度。以注册为例:
- 简要说明要写清"谁能做什么、做完之后还能做什么"。原文写的是填六项信息即可注册,注册后可以继续补详细信息和收货人信息——这半句其实是在给后续用例埋伏笔,说明注册用例的后置状态里"用户已登录"是默认成立的。
- 基本流按"用户动作 / 系统响应"交替写,一步一个动作。注册的基本流是六步,从选择注册到系统提示成功并返回首页,节奏是对的。
- 备选流按失败原因分类,不是按步骤编号罗列。原文给了三类:字段校验不通过、用户名重复、系统异常。这三类分别对应输入层、业务层、基础设施层,覆盖得比较完整。
- 前置条件和后置条件写的是状态,不是动作。前置"游客申请注册",后置"游客注册成功成为会员",一进一出,状态是闭合的。
我一般会在这个模板上再加一列"触发频率",因为低频用例(比如找回密码)和高频用例(餐品检索)在设计阶段的关注点完全不同,前者重健壮性,后者重响应时间。这一列不加也能交作业,但要拿去做真实项目评审,缺了它讨论就没落点。
提示:后置条件不要写"用户成功浏览餐品信息"这类和用例名同义反复的句子,要写成系统里可观察到的状态变化,比如"检索结果集写入会话缓存并返回前 20 条"。这样顺序图里才有东西可画。
2.3 十二个用例的排期:先画哪条主线,后补哪些枝节
十二个用例一起开工,画到第八个就会发现前面的类图得改。合理的顺序是沿着依赖链推:
| 批次 | 用例 | 依赖 | 为什么排这个位置 |
|---|---|---|---|
| 第一批 | 注册、登录/注销 | 无 | 身份是所有权限判断的前提,user、guest 两个类在这一批定稿 |
| 第二批 | 餐品信息检索、餐品选购 | 依赖登录 | 引出 dish、orderlist 两个核心类,购物车的合并规则在这一批必须敲定 |
| 第三批 | 订单信息管理 | 依赖选购 | 订单状态机在这里成型,order 类定稿,管理员端并行 |
| 第四批 | 管理员餐品管理、用户权限管理、用户信息管理 | 依赖身份与餐品 | 管理端补全,冻结/解冻、上下架规则落位 |
| 第五批 | 留言板、公告栏、收藏夹、餐品评论 | 依赖身份 | 附属功能,结构高度相似,可以批量复用顺序图模板 |
这么排的理由很实在:每批产出的类都能被下一批直接引用,返工量最小。反过来先画留言板再画订单,留言板的 comment 类和订单的 order 类之间没有任何关系,等订单状态机一变,前面画的顺序图里的消息编号就全乱了。
第一批定稿的标志我一般看两个东西:user 类的属性表能不能支撑注册的六项信息(用户名、密码、邮箱、电话、安全问题、答案),以及 guest 类到底需不需要独立存在。报告里 guest 是一个独立类,只关联 comment 和 favorite,多重度都是 0..* 对 0..1。这个设计在分析阶段说得通——游客是一个未认证的身份占位——但到了设计阶段,它大概率会退化成 user 表里的一个状态字段。这不是错误,是阶段性抽象,评审时能讲清楚为什么保留它,比偷偷删掉更显功力。
3. 分析模型怎么填血肉:架构、机制表与九个关键抽象
需求模型回答"系统做什么",分析模型回答"这些事由谁承担、怎么承担"。这一层如果只画一张类图交差,后面写顺序图会非常难受,因为找不到消息该发给谁。
3.1 架构模型只有四块:Client、Server、DB Server、Printer
报告里的架构模型列了 ClientSystem、Server、DB Server、Printer 四部分。前三个是标准三层的雏形,第四个 Printer 容易被当成凑数——但对照订单信息管理的顺序图第 6 步"生成订单打印",它是有出处的:订单确认后要出纸质单据。
把 Printer 画进架构图,好处是它把"外部设备"这个边界显式化了。订单打印失败和订单创建失败是两回事,前者不能让整笔订单回滚。这条判断如果不提前在架构层点出来,编码阶段极容易写成"打印异常 → 抛异常 → 事务回滚 → 用户以为没下单成功",然后重复下单。
约束上,Client 只做展示和输入校验(字段长度、必填项),Server 承担控制层与业务规则(用户名唯一、购物车数量合并、订单状态流转),DB Server 只负责持久化。这条分工对应到顺序图,就是"控制层"和"信息保存层"两条竖向泳道,消息一来一回,层次不能串。
要注意的是,这张图严格来说混了部署视图和逻辑视图。做作业无所谓,真拿去评审,我会拆成两张:逻辑分层图放分析模型,部署图放设计模型,后者再补上网络边界和节点数量。混在一起画的后果是,后面讨论"订单服务要不要独立部署"时,图上没有对应的位置可以指。
3.2 分析机制表:九行机制,每一行都要能落到实现手段
报告里那张"分析类—分析机制"的对照表,是全篇最容易被跳过、实际上最该逐行读的部分:
| 分析类 | 声明的机制 | 具体落法 | 怎么验证 |
|---|---|---|---|
| orderlist | Persistency、Security | 订单明细独立成表,主键由订单号加行号组成;只有订单归属人可读写 | 换账号访问他人订单明细应返回拒绝 |
| system | Persistency、Legacy Interface | 系统参数表;对外部支付/旧系统留一层适配接口 | 桩实现可切换,断开外部依赖仍能跑通主干 |
| order | Persistency、Security | 订单头表带状态字段与版本号;状态变更需权限校验 | 并发发货请求只能成功一次 |
| dish | Persistency、Distribution | 餐品表读多写少,可加只读副本或本地缓存 | 上下架后缓存失效时间可观测 |
| user | Persistency、Redundancy | 用户表;收货信息在下单时快照进订单,不实时关联 | 用户改地址后历史订单地址不变 |
| guest | Persistency、Security | 访客会话或匿名标识;权限最低 | 未登录访问收藏接口被拦截 |
| favorite | Persistency、Communication | 收藏表;收藏变更异步通知推荐模块 | 消息队列有对应事件 |
| notice-board | Persistency、Communication | 公告表;发布后推送或轮询 | 新公告在约定时限内可见 |
| comment | Persistency、Parsing | 评论表;内容需长度与敏感词解析 | 少于 8 字符的留言被拒 |
这张表的价值在于,它逼你在画类图的时候就想清楚每个类将来会遇到什么非功能要求。比如 dish 标了 Distribution,那么设计阶段就不会天真地认为改完数据库所有客户端立刻可见;user 标了 Redundancy,就直接解释了为什么 orderlist 里冗余存了 foodname 而只存 dishID——餐品改名或下架之后,历史订单还得能显示当时下单的名字。
Legacy Interface 这一条也值得单说。system 类承担旧接口适配,意味着系统里要有一个防腐层,外部接口的字段名、编码、超时策略都封在这一层里,不往业务层渗。做法很朴素:定义一个稳定的内部接口,适配器负责转换,外部改版时只改适配器。
3.3 九个关键抽象与多重度:0..* 对 0..1 之外还该有什么
报告建立了 orderlist、system、order、dish、user、guest、favorite、notice-board、comment 九个类。核心关系大致是这样:
@startuml class User { username password telephone address city } class Guest class Order { ordernum username time address telephone status total } class OrderList { foodname foodnum fooddiscout items } class Dish { dishID dishname dishprice markprice meat cooking hot recommend } class Comment { title content name telephone } class Favorite class NoticeBoard { title content activity activitytime } class System User "1" -- "0..*" Order : 下单 Order "1" -- "1..*" OrderList : 包含明细 OrderList "0..*" -- "1" Dish : 引用 User "1" -- "0..*" Comment : 发表 User "1" -- "0..*" Favorite : 收藏 Favorite "0..*" -- "1" Dish : 指向 Guest "1" -- "0..*" Comment : 匿名浏览 NoticeBoard "0..*" -- "0..1" Dish : 关联推广餐品 @enduml这段描述的是语义关系,不是最终物理模型。关键点三处:
第一,Order 与 OrderList 是一对多,OrderList 是订单明细行,不是"购物车列表"。报告里 orderlist 的属性是 foodname、foodnum、fooddiscout、items,明显是一行餐品的快照;而 order 的属性是 ordernum、username、time、address、telephone,是订单头。把这两个混成一个类,订单里买三样菜就没法表达了。
第二,OrderList 与 Dish 之间是多对一引用,但同时冗余了 foodname。这就是上一节说的快照:管理员删除餐品时"对应的订购记录仍然保留",靠的就是这份冗余。只留 dishID 的话,删完菜历史订单就成了空白行。
第三,大部分关系被写成 0..* 对 0..1,这个多重度偏保守。一个订单至少有一条明细,写 1..* 更准确;一个收藏项必然指向一个餐品,也应该是 1 而不是 0..1。多重度写松了不会报错,但会在评审时被问"什么时候会是 0",答不上来就是硬伤。
3.4 类属性落地:从 dish 的十个字段看数据字典
dish 类是属性最多的一个:dishID、dishname、dishprice、markprice、meat、cooking、material、discription、hot、recommend。逐个看它们的来历,能反推出需求里的信息:
| 字段 | 类型 | 约束 | 对应需求 |
|---|---|---|---|
| dishID | int | 主键、自增 | 管理员检索与删除的定位依据 |
| dishname | string | 非空、唯一 | 检索条件之一 |
| dishprice | double | 大于 0 | 网订价,参与总价计算 |
| markprice | double | 大于等于 dishprice | 市场价,用于展示折扣 |
| meat | boolean | 非空 | 是否荤菜,用于筛选 |
| cooking | string | 枚举 | 做法描述,检索结果展示项 |
| material | string | 可空 | 主要材料,检索结果展示项 |
| discription | string | 可空 | 简介,检索条件之一 |
| hot | boolean | 默认 false | 被订购次数是否过阈值 |
| recommend | boolean | 默认 false | 店长推荐标记 |
这里有一处需求与模型的错位值得记下来:餐品信息检索的基本流第 6 步要求用户能看到"被订购次数",但 dish 类里只有一个布尔型 hot,没有计数字段。要么把 hot 改成 orderCount 整数,要么在订单明细里聚合计算。前者写起来简单但需要定时刷新,后者实时准确但每次检索都要 join。选哪个取决于检索页的并发量,我一般选前者加缓存,因为检索页是读多写少,聚合查询容易成为瓶颈。
user 类的属性是 username、password、telephone、address、city,加上注册时填的邮箱、安全问题、答案,一共八项。注意 address 和 city 拆成了两个字段,而不是一个 address 字符串,这在后面订单快照时有用——按城市分区统计订单量的时候不用做字符串解析。
comment 类只有 title、content、name、telephone 四个属性,缺了时间戳和所属餐品 ID。这是模型里的一个缺口,也是下一章顺序图对不上的根源之一:餐品评论用例要求"在对应的餐品页面"评论,但 comment 类里没有 dish 外键。补法是加 dishID 与 createTime 两个字段,历史数据显示为默认值。
4. 用例实现怎么写:顺序图与控制层、信息保存层的对位
到了这一层,文字描述和图必须严丝合缝。报告给每个用例都配了顺序图,消息编号统一到 7 步左右,结构高度一致,读起来很顺。
4.1 顺序图的固定三段:界面层、控制层、信息保存层
所有顺序图都是同一个骨架:用户发起,界面层接收,控制层校验与编排,信息保存层落库,然后原路返回。以注册为例,7 条消息是这么分的:
- 用户 → 注册界面:选择注册
- 用户 → 注册界面:填写注册信息
- 注册界面 → 控制层:提交注册信息
- 控制层 → 信息保护层:保存用户信息
- 信息保护层 → 控制层:保存成功
- 控制层 → 注册界面:保存成功
- 注册界面 → 用户:返回注册结果
读法上有个细节要注意:第 5 和第 6 条在原文里都写成"用户信息保存成功",看起来重复,实际是两个不同的返回边——一个是持久层回给控制层,一个是控制层回给界面层。画图时靠激活条区分,写文档时如果两条消息文字完全一样,评审的人就会以为画错了。我的习惯是把返回消息写成"保存结果(成功/失败)",用括号带状态,避免歧义。
三段式的好处是每一层职责单一:界面层不写业务判断,控制层不碰 SQL,持久层不做权限判断。坏处是消息条数会变多,简单用例也得画七条。对作业来说这是规范,对真实项目来说,简单查询一般会省掉控制层直接走仓储,但订单创建这类有状态流转的必须走三层。
4.2 注册与登录:两套入口,同一个控制层
注册的前置是游客未登录,后置是"注册成功并返回首页(默认已登录)"。这个默认已登录的行为在顺序图里体现为:第 7 步返回之后,界面层直接跳到首页而不是登录页。很多实现会老老实实跳回登录页让用户再输一次,体验差一截,但也没错——关键是要在文档里写清楚选了哪条路,别图和字两套说法。
登录/注销的用例描述比注册复杂,因为它塞了三件事:普通用户前台登录、管理员后台登录、密码找回。顺序图只画了 7 步主流程,找回密码在备选流里一句话带过。这在作业里能过,在评审里会被追问"找回密码的控制流去哪了"。补法是单独画一条备选流的顺序图,或者在主图里加一个 opt 片段,标明条件。
管理员登录有一处硬约束:只能在后台运行。这条要在架构上体现为独立入口,控制层要用不同的拦截器。如果前后台共用一套登录接口,靠一个 role 字段区分,那么"管理员必须凭管理员权限登录后台"这句话在代码里就没有对应的检查点,只能靠前端菜单隐藏,属于典型的假安全。
验证码这一项在模型里没有对应的类或字段。它属于会话级数据,不落库,一般放缓存并设过期时间。文档里如果不提,实现时容易被省略。
4.3 购物车与订单状态机:两个顺序图之间的状态鸿沟
餐品选购的顺序图描述了添加、移除、清空、统计、结算五个动作,其中有一条规则必须写死在控制层:再次添加已存在的餐品,不新增行,而是数量累加。这条规则决定了 orderlist 的唯一键是"订单号 + 餐品 ID",而不是自增行号。如果控制层写成无脑 insert,购物车里会出现两行同名餐品,结算总价算两次,用户一看金额不对就跑了。
订单信息管理的顺序图分了两张,一张用户侧、一张管理员侧。用户侧的关键在第 4 到第 6 步:确认收货信息、提交订单、生成订单并打印。管理员侧画了未完成、已发货、已完成、取消四条支线,消息编号拉到 20 条,是全篇最长的一张。
把两边的状态词对齐,会发现命名有两套:用例描述里叫待发、已发、已完成、已撤销,顺序图里叫未完成、已发货、已完成、取消。语义能对上,但字面不一致,写代码时容易一个枚举写四遍。统一成四个状态加一次迁移检查:
| 当前状态 | 触发动作 | 触发者 | 目标状态 | 是否可逆 |
|---|---|---|---|---|
| 待处理 | 发货 | 管理员 | 已发货 | 否 |
| 待处理 | 撤销 | 管理员或用户 | 已撤销 | 否 |
| 已发货 | 用户确认签收 | 用户 | 已完成 | 否 |
| 已发货 | 撤销 | 不允许 | — | 需管理员介入 |
| 已撤销 | 任何动作 | — | — | 否 |
落到表结构上,状态字段配版本号,防止两个管理员同时点发货:
-- 订单头表:状态 + 版本号,乐观锁防并发重复发货 CREATE TABLE orders ( order_no VARCHAR(32) PRIMARY KEY, username VARCHAR(64) NOT NULL, receiver VARCHAR(64) NOT NULL, telephone VARCHAR(20) NOT NULL, address VARCHAR(255) NOT NULL, create_time BIGINT NOT NULL, -- 毫秒时间戳,按降序检索 status TINYINT NOT NULL DEFAULT 0, -- 0待处理 1已发货 2已完成 3已撤销 version INT NOT NULL DEFAULT 0, KEY idx_status_time (status, create_time DESC) -- 支撑按状态分类 + 时间降序 ); -- 订单明细:餐品名称做快照,餐品下架后历史订单仍可读 CREATE TABLE order_list ( order_no VARCHAR(32) NOT NULL, dish_id INT NOT NULL, food_name VARCHAR(128) NOT NULL, -- 下单时快照,不随 dish 表变更 food_num INT NOT NULL DEFAULT 1, unit_price DECIMAL(10,2) NOT NULL, PRIMARY KEY (order_no, dish_id) ); -- 发货:带版本号条件更新,影响行数为 0 说明已被他人改过 UPDATE orders SET status = 1, version = version + 1 WHERE order_no = ? AND status = 0 AND version = ?;第一张表的索引对应"按订单状态分类检索、按生成时间降序排序"这条需求,复合索引把状态放前面、时间放后面并倒序,一次扫描就能出结果。第二张表的主键选了订单号加餐品 ID 的组合,正好把购物车的数量合并规则在存储层再兜一道——即使控制层写漏了,数据库也会因为主键冲突而失败,而不是默默多出一行。最后那条更新语句加了 status 与 version 双条件,两个管理员同时点发货时只有一个能成功,另一个影响行数为 0,前端提示"订单状态已变更,请刷新",这就是后悔药的写法。
撤销这条路径有个容易忽略的点:撤销后订单不从记录里删除,只改状态。原因是财务对账和用户查历史都要用。同理,管理员删除餐品时,order_list 里的 food_name 是快照,不受影响。
4.4 留言板、收藏夹、评论:三张顺序图其实是一张
这三个用例的顺序图结构几乎一样:选择界面、提交内容、控制层校验、持久化、返回。差异只在业务规则上:
- 留言板:正文不少于 8 个字符,只有管理员能回复,游客只能看部分留言。校验点放在控制层,长度判断放在提交之后、落库之前,这样界面层可以给出即时反馈,控制层做最终把关。
- 收藏夹:未登录不可用,支持从收藏夹直接发起订购。后一条意味着收藏项与餐品之间要能取到完整的价格和信息,所以收藏表里存 dishID 就够,不要冗余价格,否则餐品调价后收藏夹显示的还是旧价。
- 餐品评论:游客可看,登录用户可评论和回复。这里顺序图里出现了"游客登录"的消息,其实表述有误——游客既然要评论就必须先登录,那一步应该是"跳转登录"而不是"游客评论"。这类小瑕疵在批量复用时会被复制到别处,值得在定稿前统一清理一遍。
批量复用顺序图的正确姿势是:先定一个通用模板(界面、控制、持久三层,六到七条消息),再为每个用例标注差异点,最后统一编号规范。千万不要每个用例从头画,编号规则一乱,交叉引用就废了。
5. 避坑与排查:五个翻车点,每个都能在文档里找到对应位置
这一章挑的是我自己和同行在评审里真实被问住的五处。写在这里不是挑刺,是因为它们出现的概率高到几乎必然。
5.1 用例描述层的三处自相矛盾
第一处,购物车修改数量。备选流第 5 条写的是"用户修改的图书数量没有改变","图书"这个词和整个订餐系统没有关系,明显是从购物车模板复制过来的残留。现象是评审时被问"你们系统还卖书?",原因是术语没有全局替换,解决方法是交稿前用关键词检索一遍"图书、商品、宝贝"这类通用词,逐个替换成"餐品"。
第二处,餐品检索的后置条件写成"用户成功浏览餐品信息",和前置条件里的"用户在本系统中选择了餐品信息检索项"是同义反复,中间没有任何状态变化。现象是评委追问"这个用例到底改变了系统什么",原因是没有区分查询型用例和变更型用例——查询型用例的后置条件应该落在缓存、日志或会话状态上,而不是笼统的"浏览成功"。解决方法是给查询型用例单独定一条后置条件模板,比如"检索结果写入会话,命中条数记录到访问日志"。
第三处,留言板的备选流写"只有注册的用户才可以在留言板上发表留言,一般游客只能浏览部分留言",但主事件流第 2 步又写"用户登录留言板查看留言",没有区分登录和未登录。现象是画顺序图时不知道该不该画登录步骤,原因是角色分支没有在事件流里显式分支。解决方法是在基本流里只写登录用户路径,把游客路径整体挪到备选流,并在前置条件里写死"发表留言需登录"。
5.2 类图关系与多重度的四处硬伤
第一处,order 与 orderlist 的多重度写成 0..1 对 0..。现象是画出来的图里一个订单可以没有明细,评审时被问"空订单是什么业务场景",答不上来。原因是照着模板默认值画,没有按业务约束收紧。解决方法是把订单到明细改成 1..,同时在控制层加一条校验:结算时购物车为空直接拒绝,对应备选流里的"该订餐没有餐品"。
第二处,comment 类缺 dishID。现象是餐品评论的顺序图里,评论落库之后无法按餐品查询,只能全表扫。原因是评论建模时只考虑了"谁说的",没考虑"说哪个餐品"。解决方法是补外键字段并在顺序图里增加一条"传入餐品标识"的消息,编号顺延。
第三处,dish 类用 hot 布尔值表达热度,但检索用例要求展示被订购次数。现象是界面上的次数永远显示 1 或 0,原因是布尔和计数的语义混了。解决办法二选一:改成整数计数并定时汇总,或者保留布尔但改名为"是否热销"并在界面上单独展示销量数字(从明细表聚合)。两条路都要在文档里写清楚,别让实现的人自己猜。
第四处,guest 类与 user 类的关系没画。现象是注册顺序图里"游客注册成为会员"这个转换没有对应的类间关系,图上只有消息没有结构支撑。原因是分析阶段习惯把身份转换写成流程,忘了它同时是一次对象创建。解决方法是在类图里补一条 guest 到 user 的依赖并标上「注册」,或者在文档里明确说明 guest 在设计中退化为 user 的状态字段。
5.3 顺序图与用例描述对不上的排查路径
顺序图和文字描述分头写,几乎是必然对不上。我给自己的排查顺序是固定的三步:
第一步,数消息。用例基本流写了几步,顺序图就应该有对应条数的请求消息,加上等量的返回消息。登录用例基本流七步,顺序图也是七条,对得上;订单管理管理员侧基本流九步,顺序图二十条,多出来的是状态分支,属于合理扩展,但要在图注里说明哪些是主流程、哪些是分支。
第二步,查参与者。文字里出现的每个动作主体,图上必须有对应的对象。留言板用例里"管理员登录留言板"是一个独立的动作主体,但那张顺序图只画了用户和三个层,管理员那条线整个缺失。补法是加一条并列的参与者泳道,或者在图上加注"管理员流程同构,仅权限校验不同"。
第三步,对状态。用例的后置条件必须能在顺序图的最后一条返回消息里找到落点。订单撤销的后置是"标记为已撤销的订单",那么顺序图最后应该有一条更新状态的消息,而不是简单返回"操作成功"。少了这一条,读者就无法确认状态到底改没改。
排查完这三步,通常能揪出三到五处不一致。修的时候优先改文字,因为文字是需求,图是实现;但如果图里画的流程更合理,那就反过来改文字,改完在文档里留一句变更说明,别让下一版的人对着两个版本猜。
6. 交付前的三张检查表:把空档补在别人发现之前
文档写到最后一版,最耗时的不是补内容,而是确认哪些地方还空着。我现在固定用三张表过一遍,每张表十分钟,能拦掉大部分低级问题。
第一张是用例覆盖表。左边列十二个用例,右边列四个格子:有没有文字描述、有没有顺序图、前置后置条件是否闭合、备选流是否覆盖异常。填空的结果一般是:文字描述齐、顺序图缺两三个、备选流普遍偏薄。缺顺序图的往往是收藏夹和公告栏这类"简单"用例,恰恰因为简单才容易被跳过,而评审最喜欢挑简单的问。
第二张是类与表的映射表。九个类逐个对到物理表,重点看三种情况:一类拆成多表的(order 与 orderlist)、多类合并成一表的(guest 并入 user)、没有对应表的(system 参数表要不要建)。这一步做完,字段类型和约束基本就定了,后面写建表语句不会返工。
第三张是消息编号校验表。把每个顺序图的消息编号抄成一列,检查三件事:编号是否连续、请求与返回是否成对、跨图的编号引用是否一致。报告里注册图出现了两条编号相同语义不同的"保存成功",就是这张表能直接抓出来的。
| 检查表 | 检查项 | 常见缺口 | 修法 |
|---|---|---|---|
| 用例覆盖 | 12 个用例 × 4 项 | 收藏夹、公告栏无顺序图 | 套用通用三层模板补齐 |
| 类表映射 | 9 个类 × 表 | comment 缺外键、dish 缺计数字段 | 补字段并回写类图 |
| 编号校验 | 每图 6–20 条消息 | 返回消息文字重复、分支未标注 | 返回消息带状态、分支加条件说明 |
有个血泪经验:这三张表不要在最后一天做,要在画完第三批用例之后就做一次,那时候改类图成本最低。拖到最后,一个字段改动会牵动七八张图和十几段文字,改到后面自己都不记得哪张图是准的。
从那以后我每次交设计文档,都会在最后半小时强制走一遍"随机挑一个用例,从事件流第一步追到顺序图最后一条返回消息",追通就过,追不通就从头再排。这个习惯救过我至少三次答辩现场。文档这东西,评审的人不会全看,但他随手翻到的那一页必须是通的。希望帮到你。
本文还有配套的精品资源,点击获取