简介:面向软件工程课程实验与UML建模练习的网上订餐系统分析设计文档,适合需要完成建模大作业、梳理需求到设计表达的学生与初级开发者参考。整份材料围绕游客、注册用户与管理员三类角色,覆盖用户权限、餐品管理、留言板与公告栏、餐品选购收藏及订单维护等业务,并给出需求模型、架构模型、分析机制与关键抽象之间的关系。资源包仅含1个doc文件,大小约149KB,便于直接查阅和摘录;文档内以用例图、顺序图、类图及用例实现说明为主,能帮助读者对照注册、登录/注销、餐品信息检索等典型场景,理解事件流、前置条件、后置条件与异常分支的写法。已有4229人学习,可作为课程实验报告的结构范本和UML图例参考,也可用于检查类属性、方法及类间多重关系的建模完整性。
1. 一份能对着抄的 UML 网上订餐系统实验报告,先看它到底解决了什么
建模作业最常见的翻车方式不是图画得丑,而是图画完了却说不清业务——类图九个框摆在那儿,顺序图七条消息连成串,被问一句“这条消息为什么从控制层发到信息保护层”就卡住了。这份 UML 网上订餐系统文档值得拿来看的地方,正在于它把需求模型、分析模型、用例实现三段串成了一条线:十二个用例每个都写了简要说明、事件流、前置条件、后置条件,再配一张顺序图;九个关键类带着完整属性表和多重性标注;分析机制一栏还逐个标了 Persistency、Security、Legacy Interface 这类职责标签。
它适合两类人:一类是正卡在课程设计上、需要一份结构完整可以对照修改的建模样本;另一类是已经写过不少代码、但没系统走完“用例—类—顺序图”这条链路,想补一次规范建模的从业者。它不是可以运行的源码,价值在于结构和方法,不是复制粘贴。
2. 需求模型拆解:三类角色、十二个用例怎么划边界
需求模型这一层最容易含糊的地方,是角色和用例的对应关系没钉死。文档把角色分成游客、注册用户、管理员三种,然后按“谁能发起、谁只能看、谁必须在后台”把十二个用例铺开。这个划分方式本身不复杂,但把它写成一张可核对的矩阵,后面画用例图、写事件流就不会来回改。
2.1 先把角色-用例矩阵钉死,再动手画图
文档的需求模型里并列出现了“管理员”和“经理”两个词,但翻到后面的用例实现,管理侧的职责全部落在 administrator 身上,没有第二个后台角色。这种情况在实际文档里非常普遍,处理方式只有一个:以用例实现章节为准,把角色收敛成三个,其余称呼统一改名。我一般会先做一张矩阵表,把“谁能发起、入口在前台还是后台”写清楚,再开始画。
| 用例 | 游客 | 注册用户 | 管理员 | 入口 |
|---|---|---|---|---|
| 注册 | 发起 | — | — | 前台 |
| 登录/注销 | 仅前台登录页 | 前台登录、注销 | 后台登录,可前后台切换 | 前台/后台 |
| 餐品信息检索 | 可检索 | 可检索 | 后台按名称/简介检索 | 前台/后台 |
| 留言板管理 | 仅浏览部分留言 | 发表留言、浏览 | 查看、回复、删除 | 前台/后台 |
| 收藏夹管理 | 无此功能 | 增、删、查、直接下单 | — | 前台 |
| 餐品选购 | 拦截并跳登录 | 购物车全流程 | — | 前台 |
| 餐品评论 | 只读 | 评论、回复他人 | — | 前台 |
| 用户权限管理 | — | — | ID 精确检索、冻结、解冻 | 后台 |
| 用户信息管理 | — | 改资料、改密码、邮箱找回 | — | 前台 |
| 公告栏管理 | 只读 | 只读 | 发布、更新、删除 | 前台/后台 |
| 管理员餐品管理 | — | — | 检索、上架、修改、下架 | 后台 |
| 订单信息管理 | — | 提交订单、撤销未发货订单 | 五种状态检索、发货、撤销 | 前台/后台 |
这张表有三个隐藏信息值得注意。第一,注册只对游客开放,管理员“不提供注册功能,只能在数据库中添加”,这条约束直接决定了后台权限表要靠初始化脚本灌数据,不能靠页面。第二,收藏夹对游客完全关闭,所以“加入收藏”按钮在未登录态必须有拦截,这在事件流的备选流里对应“未登录的游客不会拥有收藏夹功能”。第三,留言板把“发表”和“回复”拆给了两个角色——注册用户只能发,回复权只在管理员手上,这类非对称权限如果矩阵里不写清楚,画顺序图时很容易让用户侧多出一条回复消息。
2.2 事件流的写法:基本流管正常路径,备选流管边界
文档里十二个用例的写法高度统一:简要说明、事件流(基本流 + 备选流)、前置条件、后置条件、扩展点、顺序图。这套模板看着繁琐,真正值钱的是备选流和前后置条件这三块,因为它们把“系统在什么情况下会拒绝”写死了。
以餐品选购为例,基本流五步走得很干净:添加餐品(数量必填,不填默认 1;重复添加同一餐品不新增条目,而是数量累加)、移除餐品、清空购物车、价格统计(每次操作后重算单品应付与总价)、结算(把当前订餐列表提交,用户补全收货信息后生成订单)。这五步里最有含金量的是“重复添加即累加”这一条,它其实规定了购物车的主键是“用户 + 餐品”,而不是“每次添加生成一行”。照着这条去设计表结构,后面就不会出现同一个菜在购物车里挂三行的问题。
备选流一共列了七条,覆盖了未登录、非会员、购物车为空、数量未改动、用户取消修改、系统异常这几类。我见过很多同学写备选流只写“系统异常,提示错误”,这句话等于没写——异常发生在哪一步、异常之后数据是回滚还是保留草稿,才是需要写清楚的。
注册用例是另一个典型。基本流六步:选择注册、返回注册页、填六项信息(登录用户名、密码、联系电子信箱、联系电话、安全问题、答案)、系统验证、提交、提示成功并返回首页且默认已登录。三条备选流分别对应:字段长度等校验不一致、用户名已被注册、系统维护无法注册。注意最后那句“默认已登录”——它把“后置条件:游客注册成功成为会员”落到了具体行为上,注册完不用再跳一次登录页。这种前后置条件和事件流互相咬合的细节,是评审时最容易被追问的地方。
前置条件和后置条件的写法也有讲究。餐品选购的前置条件列了四条,其中“购物车中含有餐品”“购物车内不能为空”其实是针对不同操作的:清空购物车和结算的前置不一样,文档把它们并列写在一起,读的时候要自己拆开对应。后置条件那边写的是“用户添加餐品”“用户打开购物车页面”“相应餐品除”“购物车内相应餐品数量被修改成功”“购物车被成功清空”,逐条对应基本流的每一步,这种一一映射的写法值得照搬。
2.3 顺序图的三层结构:控制层和信息保护层各自管什么
每个用例的顺序图都是同一种骨架:参与者 → 用户界面 → 控制层 → 信息保护层,消息编号从 1 排到 7,形成“请求上行、结果下行”的对称结构。以注册为例,七步是:选择注册界面、填写注册信息、提交注册信息、保存用户信息、用户信息保存成功、用户信息保存成功、返回给用户注册的信息。
这里的信息保护层,实际上承担的是持久化和安全校验两件事——保存用户信息时既写库,也要处理密码存储和安全问题这类敏感字段。控制层则负责流程编排和输入校验,比如用户名是否已注册、字段长度是否超限。把这两层分开画,好处是后面换成三层架构落地时,控制层直接对应 Controller,信息保护层对应 Repository 或 DAO,职责不用重新划分。
餐品信息检索那条顺序图更能说明问题:选界面、填条件、提交、在数据库中检索餐品信息、返回餐品信息、返回餐品信息、返回餐品信息。三步返回看起来冗余,实际表达的是数据从持久层逐级向上传递的过程。画的时候保留这种逐级返回,比画一条“数据库 → 界面”的直连箭头更符合分层语义。
3. 分析模型与类图:九个关键抽象怎么落成可编码的属性
需求模型解决“做什么”,分析模型解决“拿什么对象去做”。这一层的三块内容是架构模型、分析机制、关键抽象,文档给的颗粒度比一般实验报告细,尤其是分析机制那张表,把每个类该承担哪种横切职责标了出来。
3.1 架构模型:四个组件各自的职责边界
架构模型只画了四个框:ClientSystem、Server、DBserver、Printer。前三个是标准的前后端加数据库,第四个 Printer 是订餐场景特有的——订单确认后要打印出这张订单的所有信息,对应订单信息管理用例基本流里的“提示操作完成,并打印出这张订单的所有信息”。
四个组件的分工可以这样理解:ClientSystem 承载前台页面和后台管理页面,负责展示与输入;Server 承载控制层,负责校验、编排、调用信息保护层;DBserver 负责持久化全部业务实体;Printer 是订单输出的外设通道,挂在服务端而不是客户端,因为订单打印发生在管理员或收银侧,不是用户自己的机器。
这套划分对后面的类图有直接影响:Printer 不拥有业务数据,所以它不会出现在关键抽象里;DBserver 也不作为类出现,它的能力通过分析机制里的 Persistency 分散到各个实体类上。新手常犯的错是把“数据库”画成一个类再让所有实体指向它,结果类图里多出一个和业务毫无关系的节点。
3.2 分析机制:Persistency、Security 这些标签到底标给谁
文档的分析机制表是这份材料里信息密度最高的一块,逐类标注如下:
| 分析类 | 承担的机制 | 落点说明 |
|---|---|---|
| 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 | 评论持久化,内容需做解析与过滤 |
这张表的价值在于,它逼你在画类图之前先回答“这个类会不会被并发读”“这个字段要不要加密”“这块内容要不要解析清洗”。比如 dish 标了 Distribution,意思就是餐品列表是读多写少的典型,前台检索、后台管理、购物车回显都在读它,落地时就要考虑缓存而不是每次直查。comment 标了 Parsing,对应的是评论内容的合法性检查——文档在留言板用例里明确写了“留言字符长度不得少于 8 个字符”,这就是最基础的解析规则。
我一般会在这个阶段补一列“失败表现”,写清楚这个机制没做到会出什么问题。比如 order 的 Security 没做到,表现就是订单金额能被前端改;user 的 Redundancy 没做到,表现是账号表挂了之后没有任何恢复路径。这一列写出来,类图的属性设计就有了依据。
3.3 关键抽象与多重性:0..1 和 0..* 该怎么读
关键抽象一共九个:guest、comment、favorite、orderlist、system、order、dish、user,加上承担管理职责的 administrator,以及公告 notice-board。它们之间用 0..* 和 0..1 两种基数互连,读法是固定的:站在一端看另一端,句子化成“一个 X 对应几个 Y”。
按业务语义还原,几条主线关系是这样的:
- 一个 user 对应零到多个 order(0..*),一个 order 只属于一个 user(0..1)
- 一个 order 对应一到多条 orderlist 明细,一条明细只属于一个 order
- 一条 orderlist 明细指向一个 dish,一个 dish 可以出现在多条明细里
- 一个 user 对应零到多条 comment、零到多条 favorite
- 一条 favorite 指向一个 dish,用于收藏夹里直接下单
- guest 状态可以产生 comment 和 favorite 的挂起记录,注册后归并到 user 名下
- administrator 对应零到多条 notice-board 公告
基数画错的典型症状是两头都写 0..*,比如 user 和 order 两端都标多,读起来就变成“一个订单属于多个用户”,业务上直接不成立。判断方法很简单:把关系读成一句中文,读不通就是标错了。
3.4 九个类的属性清单:能不能直接转成建表语句
文档给每个类都列了属性,颗粒度足够直接映射成数据表:
| 类 | 属性 | 转表时的注意点 |
|---|---|---|
| user | username、password、telephone、address、city | telephone 标成了 int,手机号带前导零或超长会溢出,实际应存字符串 |
| guest | name | 类型标成 int,游客标识应是字符串或会话 ID |
| dish | dishID、dishname、dishprice、markprice、meat、cooking、material、discription、hot、recommend | 市场价 markprice 与网订价 dishprice 两套价格并存,折扣在明细里算 |
| orderlist | username、ordernum、time、foodname、foodnum、fooddiscout、items | 同时存了 foodname 和 items,属于明细行冗余快照,下单后餐品改名不影响历史订单 |
| order | 由 orderlist 聚合而成,含收货人与送达信息 | 送达地址、接收时间、收货人、联系电话四要素缺一不可 |
| comment | title、content、name、telephone | 评论带联系方式,属于个人信息,展示时要脱敏 |
| notice-board | title、content、dish、activity、activitytime | 系统公告与餐品公告共用一张表,靠 activity 字段区分 |
| favorite | 用户与餐品的关联关系 | 需要联合唯一约束,避免同一餐品重复收藏 |
| system | 系统级配置与接口参数 | 涉及遗留接口对接,字段需预留扩展位 |
orderlist 里那几个字段值得单独说。foodname、foodnum、fooddiscout 加上 items 数组,本质是把下单那一刻的餐品名称、数量、折扣做成快照存进明细,而不是只存 dishID 去关联查询。这个设计在订餐场景里是对的:菜名改了价、下了架,历史订单还得原样显示。文档里管理员餐品管理那条也印证了这点——“删除以后,对应的该餐品的订购记录仍然保留,方便日后查询”,说明订单明细和餐品表之间是弱引用。
4. 把 .doc 里的图变成可复现的建模脚本
文档里的图是嵌在 Word 里的,改一次要重画一次,评审提一条意见就得返工。比较省事的做法是用文本化建模工具把类图和顺序图脚本化,图从脚本生成,改字段只改一行文本。PlantUML 是这类场景里最省事的方案,不需要装 IDE,一个 jar 包加一个文本文件就能出图。
4.1 类图脚本:九个类与关联基数一次成型
@startuml ' 类名与属性严格对齐实验报告的类设计描述,避免文档与图两张皮 skinparam classAttributeIconSize 0 class user { username : string password : string telephone : string address : string city : string } class guest { name : string } class dish { dishID : int dishname : string dishprice : double markprice : double meat : boolean cooking : string material : string description : string hot : boolean recommend : boolean } class orderlist { ordernum : int time : int foodname : string foodnum : int fooddiscount : double items : arraylist } class order { receiver : string telephone : string deliverAddress : string deliverTime : int } class comment { title : string content : string name : string } class favorite { addTime : int } class noticeboard { title : string content : string activity : string activitytime : int } class system { configKey : string configValue : string } ' 关联基数按业务语义单向读出,每条都对应一句可念通的中文 user "1" -- "0..*" order : 下单 order "1" -- "1..*" orderlist : 包含明细 dish "1" -- "0..*" orderlist : 被订购 user "1" -- "0..*" comment : 发表 user "1" -- "0..*" favorite : 收藏 dish "1" -- "0..*" favorite : 被收藏 guest "1" -- "0..*" comment : 游客留言 system "1" -- "0..*" noticeboard : 发布公告 @enduml这段脚本有两个地方是刻意改过的。一是把 telephone 从 int 改成 string,把 guest.name 从 int 改成 string,把 discription 改成 description,把 fooddiscout 改成 fooddiscount——原文档这几处属于笔误,照抄进建表脚本会直接报类型错误或字段名对不上。二是把 notice-board 类名改成 noticeboard,因为 PlantUML 里连字符会被当成语法符号,带横线的类名要么加引号要么去掉横线,加引号在后续引用时容易漏。
skinparam classAttributeIconSize 0这一行是为了关掉属性前面的可见性图标,输出更接近实验报告里那种纯文字方块图。基数标注写在引号里,格式是"近端基数" -- "远端基数",左边读右边、右边读左边各读一遍,两句都通才算标对。
4.2 顺序图脚本:注册与订餐结算两条主线
@startuml ' 注册用例:三层结构与实验报告顺序图保持一一对应 autonumber actor 游客 as G boundary 注册界面 as UI control 控制层 as C entity 信息保护层 as E G -> UI : 选择注册界面 UI -> C : 请求注册页 C --> UI : 返回注册表单 G -> UI : 填写六项注册信息 UI -> C : 提交注册信息 C -> C : 校验字段长度与用户名唯一性 alt 校验不通过 C --> UI : 返回错误提示 UI --> G : 停留在注册页 else 校验通过 C -> E : 保存用户信息 E --> C : 保存成功 C --> UI : 注册成功 UI --> G : 返回首页并置为已登录 end @endumlautonumber会自动给消息编号,省掉手写 1 到 7 再回头调整顺序的麻烦。alt/else块对应的是注册用例的三条备选流,把“校验不通过”和“校验通过”显式分叉,比在文档里另起一段文字描述更直观。boundary、control、entity 三个关键字分别把参与者框渲染成界面、控制、实体三种形状,正好对应文档里的“用户界面—控制层—信息保护层”三层。
订餐结算那条主线同理,起点是收藏夹或购物车,终点是生成订单并打印,中间的“填写送达信息”是唯一需要人工介入的步骤,对应文档里订单确认的四要素。
4.3 文档与模型同步:命名、编号、导出
脚本化之后要保持三样东西同步:类名、消息编号、图题。类名统一用英文小写加下划线,中文名只出现在图题和文档说明里;消息编号按用例内从 1 开始,不跨用例连续编号;图题格式固定成“用例名 + 顺序图”,方便在文档里交叉引用。
导出这一步有几个参数必须带上:
# 批量导出 PNG,-charset 必须显式指定,否则中文标签会变成方框 java -jar plantuml.jar -charset UTF-8 -tpng diagram/*.puml # 同时导一份 SVG,插进 Word 后放大不失真,答辩投影更清楚 java -jar plantuml.jar -charset UTF-8 -tsvg diagram/*.puml-charset UTF-8是最容易漏的一个,漏了之后中文类名全部乱码,而且乱码是在导出后才发现的,改起来要重跑一遍。-t后面的 png 和 svg 是输出格式,需要 PDF 就换成-tpdf。目录参数支持通配符,把所有 .puml 放在同一个目录下一条命令全出。
5. 避坑与排查:十二个用例最容易翻车的五个地方
这份文档的结构已经比较完整,但直接照着抄仍然会踩到几类问题。下面五条是我逐节核对时反复遇到的,每条都按“现象 → 原因 → 解决”说清楚。
5.1 需求模型阶段的三条高频问题
事件流写成操作手册。现象是基本流里出现“点击提交按钮,页面跳转到首页”“输入框失去焦点后校验”这类描述。原因是把界面行为当成了业务步骤,写的人脑子里想的是页面,不是业务对象。解决方法是每条基本流都写成“参与者做什么 / 系统做什么”的配对句式,动词落在业务名词上,比如“用户提交注册信息 / 系统校验用户名是否已注册”,界面跳转只出现在后置条件里。
同一角色多个称呼。现象是需求模型里同时出现“管理员”和“经理”,用例实现里只剩 administrator。原因通常是不同章节由不同人写,或者从不同资料里摘抄合并。解决方法是先定一份角色表,三个角色各给一个英文标识(guest、user、administrator),全文只允许出现这三个标识和它们的中文名,其余称呼一律替换。
前后置条件与事件流脱节。现象是前置条件写了四条,但基本流只有三步,读不出来哪条对应哪步;后置条件写“用户成功下单”,但事件流里根本没写订单状态怎么变更。解决方法是后置条件严格按基本流的每一步一一对应写,几条基本流步骤就写几条后置结果,多出来的要么删掉要么补进事件流。
5.2 类图与顺序图阶段的两条结构性问题
属性挂错类。现象是 notice-board 类里出现 username、password、email、telephone、securityquestion 这五个字段。这五项实际上是管理员注册信息,属于 administrator 或 user 的职责,和公告本身没有关系。原因是抄类图时把相邻两个类的属性框连在了一起。解决办法是给每个属性问三个问题:谁产生的、谁修改的、这个类的实例被删除后它还在不在。公告被删了,管理员的邮箱还得在,那它就属于管理员。
拼写和类型笔误直接进代码。现象是 fooddiscout、discription、meat:boolec、guest.name:int 这几处原样出现在建表脚本里,跑起来报“未知类型 boolec”或者字段名对不上。原因是建模阶段只求图好看,没人把字段名单独拉一张清单核对。解决办法是单独维护一份字段术语表,中文名、英文名、类型、是否可空四列,类图和建表脚本都从这张表生成,改一次两处同步。
除了这五条,还有一处值得留意:多条顺序图里的消息编号规则不统一,有的图从 1 编到 7,有的图控制层是从 2 开始、信息保护层从 4 开始,还有的图里出现了两条完全相同的“保存成功”返回。这类不一致不影响业务正确性,但会让交叉引用变得困难。统一成“每个用例内从 1 连续编号、返回消息不单独占用编号”之后,图与图之间就能互相对照了。
6. 交付前用“后置接前置”跑一遍闭环
文档写到最后一版,最怕的不是某张图不好看,而是用例之间接不上。我习惯在交稿前做一件事:把十二个用例的后置条件全部抽出来,逐条去对下一个用例的前置条件,看这条链能不能走通。
具体做法是拉一张两列的表,左列写“某用例的后置条件”,右列写“依赖它的用例前置条件”,然后逐行核。
| 上游后置条件 | 下游前置条件 | 是否闭合 |
|---|---|---|
| 注册成功,游客成为会员并默认登录 | 餐品选购要求“已注册并已登录的会员” | 闭合 |
| 登录成功,记录为登录用户 | 收藏夹要求“用户登录后选择收藏功能” | 闭合 |
| 购物车结算生成订单 | 订单管理要求“会员购物车内不能为空” | 需补:结算后购物车是否清空要写明 |
| 管理员后台登录 | 餐品管理、权限管理、订单管理均要求后台登录 | 闭合 |
| 订单被标记为已发货 | 用户侧只能撤销“尚未发送”的订单 | 需补:撤销的时间窗口判据 |
第三行和第五行就是典型的缺口。结算后购物车清不清空,文档没写,落地时两个开发能做出两种行为;用户撤销订单的边界只写了“尚未对该订单进行发送处理之前”,但管理员发货和用户撤销如果同时发生,谁优先没定义。这类缺口不需要在建模阶段给出完整方案,但至少要在文档里标一句“此处待定”,比留白强。
还有一个纯技巧层面的习惯:所有图导出成 SVG 再插进 Word,别用 PNG。PNG 在 Word 里缩放之后线条会糊,答辩投影时顺序图里的消息文字基本看不清;SVG 是矢量格式,放到两米宽的幕布上边缘依然锐利。导出时记得带-charset UTF-8,中文类名乱码这个问题我在第一次做课程设计时踩过,改完重导一遍花了整个晚上。
从那以后,我每次交建模文档前都会强制走一遍这条“后置接前置”的链,哪怕只花二十分钟,也比评审现场被问“注册完之后用户到底是什么状态”要划算。这份订餐系统文档的结构已经足够撑起一次完整的建模训练,剩下的就是把这几处接缝自己补上。希望帮到你。
本文还有配套的精品资源,点击获取