ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

超市管理系统UML综合实验:从用例图到代码骨架的模型一致性设计

超市管理系统UML综合实验:从用例图到代码骨架的模型一致性设计 简介面向对象分析与设计UML综合实验报告——超市管理系统是一份面向高校软件工程、计算机相关专业学生及初学者的UML建模实践范本以超市日常运营为业务背景完整呈现从需求分析、参与者识别、用例建模到子系统功能设计的全流程。报告将系统划分为仓库、采购、财务、人事、销售、登录、信息管理七大子系统并逐一说明各模块功能与业务流程在用例模型部分识别了仓库管理员、收银员、采购员、会计、系统管理员、经理等角色展示不同身份如何通过权限验证进入相应子系统完成操作。针对典型业务场景报告给出了具体需求描述例如仓库管理中的商品种类与畅销品统计、采购管理中的进货单与退货单处理、财务管理中的工资奖金记录与利润报表生成等便于读者理解从业务到模型的映射方法。包内为单个docx文档约383KB含系统用例模型、功能模块图及各子系统流程图结构清晰可按章节复制标注。目前已有578人学习适合作为课程实验、毕业设计或UML自学参考。1. 一份综合实验报告最容易翻车的地方图与图互相对不上先说一个评审现场常见的翻车方式用例图画了“结算”类图里却找不到Order时序图把消息发给了购物车类图上的Cart却没有对应方法状态图里画了“已退款”代码枚举里根本没有这个状态。所谓面向对象分析与设计UML综合实验核心难点从来不是“不会画一张 uml 类图”而是分析模型、设计模型与代码模型是否自洽。这次以超市管理系统为载体把收银、补货、会员、库存这一串业务讲透再用 UML 把这套逻辑约束成可落地的设计。适合正在做课设、实训或第一次带小型业务系统的人参考。2. 用例图与活动图先钉死超市的业务边界2.1 用 uml 用例图区分参与者、系统边界与业务用例画出用例图之前先回答一个问题超市管理系统到底管理什么常见做法是先把参与者列全再去展开用例。顾客、收银员、店长、采购员是典型参与者供应商如果只提供数据不直接操作系统也可以画在系统右侧作为外部角色。参与者少画一个后续类图就少一整个模块所以这一步值得多花时间。业务用例方面登录、商品浏览、购物车、结算、退货、补货申请、库存盘点、销售统计、会员管理这九项基本能覆盖课程设计需求。注意区分include和extend会员积分抵扣是结算流程里“必定要算”的一步用include退货只在订单异常时才触发用extend。下面这段 PlantUML 可以直接跑出用例图startuml left to right direction skinparam packageStyle rectangle actor 顾客 actor 收银员 actor 店长 rectangle 超市管理系统 { usecase 结算 as UC001 usecase 退货 as UC002 usecase 会员积分抵扣 as UC011 usecase 补货申请 as UC003 usecase 盘点 as UC004 usecase 销售统计 as UC005 } 顾客 -- UC001 收银员 -- UC001 收银员 -- UC002 顾客 -- UC011 UC011 . UC001 : include UC002 . UC001 : extend 店长 -- UC003 店长 -- UC004 店长 -- UC005 enduml这段代码里include和extend是两条虚线箭头方向相反评审最容易盯的就是这里。include从扩展用例指向基用例表示“积分抵扣”作为结算的子流程必然执行extend从退货指向结算表示退货是结算的可选补充分支。用例图的价值不是展示业务流程而是划定系统边界哪些操作交给系统哪些操作留给人工这张图必须交代清楚。2.2 用例描述表把“结算”写成可验收的业务规则只有用例图远远不够一个“结算”用例在报告里至少还要配一张用例描述表否则评审无法判断你设计的系统到底做没做到位。表里写清楚参与者、前置条件、主成功场景和异常流主成功场景按编号列出异常流对应到失败节点。编号的好处是后面写时序图时可以直接引用“场景 SC-01”。用例名称结算参与者顾客、收银员前置条件购物车非空商品状态为可售主成功场景1. 收银员扫描商品条码系统累加购物车金额2. 顾客出示会员码系统按会员等级计算折扣与积分3. 系统调用支付渠道发起支付4. 支付成功后系统扣减库存并生成订单异常流2a. 商品库存不足系统提示并暂停该商品结算3a. 支付超时系统保留购物车并允许重试后置条件订单状态为待发货库存记录已更新会员积分已入账把“结算”写成这个粒度类图里该出现哪些类、哪些方法基本已经浮出水面购物车、商品、会员、订单、库存记录、支付记录一个都不会少。这也是用例驱动设计的核心思路先定义行为再反推对象。2.3 活动图补流程细节收银、退货、补货的泳道与对象流用例描述表是主干的文字版活动图负责展示分支、并发和负责角色。活动图常被当作流程图来画这是最常见的误区。UML 活动图支持泳道、对象流和并发分叉能表达某个操作由谁执行、产物是什么。以补货流程为例活动图要能看出“店长提交申请”和“采购员确认采购单”之间的先后约束。startuml |店长| start :盘点低库存商品; :生成补货申请; |采购员| :核对供应商报价; if (金额超过权限?) then (是) :提交主管审批; else (否) endif :生成采购单; |仓库| :收货并更新库存; stop enduml活动图里的泳道名就是后续包图分层的参与者来源对象流上的“采购单”则是类图里的候选类。画活动图时尽量把“谁来做”和“产出什么”标出来避免画成只有一列节点的线性流程图。对象流用带方框的对象名表示它连接两个活动说明前一个活动产出的数据会被后一个活动消费这是面向对象分析和普通流程图的本质区别。3. uml 类图与对象关系静态设计里最容易返工的三个地方3.1 面向对象分析第一步从需求里抽类砍掉伪类拿到超市需求文档后的标准动作是把所有名词圈出来再筛掉伪类。商品、分类、供应商、采购单、货架、库存、订单、订单项、购物车、收银员、会员、支付记录、退货单、盘点单、销售统计这些名词多数会成为类。以“系统”或“管理”开头的词要小心“商品管理”“库存管理”往往是职责描述不是业务实体以“信息”结尾的词大概率是多个类的属性集合也不急着建类。一个实用的筛选规则是一个候选类至少要有两个独立属性并且在用例中出现过。比如“货架”在补货用例中出现过可以建模但“登录状态”这种只存在于会话期间的概念放到Session对象里即可不必单独成类。类图里的类名用领域术语不要用“Entity”“DTO”这种技术后缀污染领域模型否则后面写代码时会把业务逻辑和技术框架混在一层里。3.2 类图关联关系聚合、组合与依赖别画错线类图是评审最仔细看的图也是最容易画错关系线的图。Order与OrderItem是组合订单删除后订单项没有独立存在的意义组合端用实心菱形Cart与Product是聚合购物车移除商品后商品依然在库存里聚合端用空心菱形CheckoutService和PriceCalculator只是运行时调用用带箭头的虚线表示依赖。startuml class Product { - String barcode - String name - BigDecimal price getBarcode() getPrice() } class Category class Supplier class StockInventory { - int quantity checkStock(barcode) : int deduct(StockEntry) : void } class PurchaseOrder class Order { - String orderNo - OrderStatus status addItem(OrderItem) : void totalAmount() : BigDecimal } class OrderItem { - int quantity - BigDecimal price subtotal() : BigDecimal } class Cart class Member { - int points calcDiscount(amount) : BigDecimal } class PriceCalculator interface class CheckoutService { checkout(Cart, Member) : Order } Order 1 *-- 0..* OrderItem Product 1 *-- 0..* OrderItem Cart * o-- * Product PurchaseOrder * -- 1 Supplier Product * -- 1 Category CheckoutService .. PriceCalculator CheckoutService .. StockInventory enduml这套类图里1和*放在关系线两端表示重数。Order 1 *-- 0..* OrderItem读作“一个订单由零到多个订单项组成”组合关系里整体和部分生命周期一致。Cart和Product如果只是“购物车里有多种商品”用o--聚合如果购物车条目本身还有数量、加购时间就把“购物车条目”单独建模成一个关联类而不是在Cart和Product之间直接连一条多对多。这个关联类的遗漏是超市类图最常见的返工点。用 Visio 画类图时注意线端形状聚合空心菱形贴“整体”一侧组合实心菱形也贴“整体”一侧。Visio 的连线默认是普通箭头需要手动改线端类型这也是“用 visio 怎么画 uml 类图”被反复搜索的根源它画静态图很快但关系语义完全靠手动维护。3.3 依赖倒置把价格计算变成接口而不是写死在收银台实验报告里如果出现“收银台类里写死会员折扣逻辑”的设计评审分数不会高。更合理的做法是定义PriceCalculator接口让普通价、会员价、促销价各自实现收银台只依赖接口。这样的收益是新增促销策略时不需要改CheckoutService的代码只新增一个实现类即可。依赖倒置在类图上表现为依赖箭头指向抽象CheckoutService .. PriceCalculator而不是CheckoutService -- MemberPriceCalculator。这个设计的直接好处是后续测试能替换实现写单元测试时不需要真实会员服务用一个假实现就能跑通结算流程。面向对象分析做到这一步类图就不只是交作业用的静态图而是能指导代码结构的约束文件。4. 时序图与状态图用动态行为反查静态设计4.1 时序图验证方法消息必须落在类图的方法上时序图最大的价值不是画“谁调谁”而是验证类图上的方法是否真的够用。以“顾客结算”为主成功场景消息序列应该覆盖类图中所有关键方法。如果发现某个消息在类图上找不到方法要么补方法要么改消息这一步是面向对象分析与设计实验中最重要的自检动作。startuml actor 顾客 participant CheckoutService as CS participant Cart as Cart participant StockInventory as INV participant Member as M participant PaymentGateway as PG 顾客 - CS: checkout() CS - Cart: getItems() Cart -- CS: 商品列表 CS - INV: checkStock(barcode, qty) INV -- CS: 可用库存 CS - Cart: getItems() CS - M: calcDiscount(amount) M -- CS: 折扣后金额 CS - PG: pay(total) PG -- CS: 支付结果 CS - INV: deduct(entries) CS -- 顾客: 结算完成 enduml注意这里的第二条消息getItems()和第五条重复出现是刻意为之结算需要先读购物车支付成功后需要再次确认明细并扣库存。时序图的消息不要只画“系统保存订单”这种抽象描述要具体到类和方法名才能与类图形成一一对照。一条消息对应类图一个公开方法这是检查设计完整性的硬标准。4.2 超市订单的状态图状态与事件分离代码才写得干净状态图描述单个对象生命周期的状态迁移典型案例是订单。订单有待支付、已支付、备货中、已发货、已收货、已取消、已退款等多个状态状态图把它们画成一个闭环每个迁移都要标注触发事件。startuml [*] -- 待支付 待支付 -- 已支付 : 支付成功 待支付 -- 已取消 : 用户取消 已支付 -- 备货中 : 库存扣减完成 备货中 -- 已发货 : 出库扫码 已发货 -- 已收货 : 顾客确认收货 已支付 -- 退款中 : 申请退款 退款中 -- 已退款 : 审批通过 退款中 -- 已支付 : 撤销申请 enduml状态图里要区分“状态”和“事件”事件是迁移的触发条件状态是事件发生后的结果。代码实现时OrderStatus枚举只保留状态事件对应服务方法不要在枚举里写支付成功这样的字段。状态图还揭示了类图可能遗漏的细节如果类图上的Order没有cancel()、refund()方法那么状态图里的“已取消”“退款中”就没有落点。4.3 动态图对类图的反馈回路改图也要改代码时序图和状态图画完要做一次双向检查正向看类图是否能支撑时序图的消息反向看状态图里的每个状态是否都能在类图的属性里找到映射。比如“待支付”在代码里体现为Order.status字段的初始值“已支付”则是pay()方法执行后的结果。只要有一个状态在类图上没有对应的属性或方法就说明静态设计漏了东西。这个阶段常见的问题是时序图和状态图各画各的。比如时序图支付成功后直接转向“已支付”但状态图里“已支付”需要依赖库存扣减完成才可以进入两个图对同一动作的约束不一致。遇到这种情况以状态图为准时序图补一条deduct()消息让模型自洽。动态图不是静态图的配图而是它的校验工具。5. uml 包图与超市系统落地从模型到可执行的代码骨架5.1 包图依赖方向展示、应用、领域、基础设施四层包图容易被低估实际上它是评审判断你有没有架构意识的关键。超市管理系统按责任分为四个包展示层的收银台界面应用层的结算服务领域层的订单、商品、价格策略基础设施层的支付网关、数据库映射。包图里只画包和包之间的依赖线依赖线要从上层指向下一层禁止出现领域层反向依赖基础设施层。startuml package 展示层 client { [收银台界面] as UI } package 应用层 application { [CheckoutService] as CS } package 领域层 domain { [Order] as Order [OrderItem] as OI [Product] as P [PriceCalculator] as PC } package 基础设施层 infrastructure { [PaymentGateway] as PG [MyBatisMapper] as Map [MySQL] as DB } UI .. CS CS .. Order CS .. PC CS .. PG CS .. Map Map .. DB enduml包图上的每条依赖线对应代码里一个 import 方向。比如CS .. PG表示应用层调用了基础设施层的支付网关这时要在CheckoutService里注入一个PaymentGateway接口而不能直接new一个支付宝实现类。依赖倒置在包图层面体现为领域层不依赖任何具体技术框架mapper、数据库连接都放进基础设施层。这样以后换数据库或换支付渠道只改外层不动领域模型。5.2 从类图生成 Java 骨架一个订单类的正反例类图画完写代码就变成翻译工作。一个合格的正例是这样的Order类持有状态枚举和订单项列表行为都封装在领域类内部外部不能直接改状态。反面例子则是把Order写成纯数据类没有方法业务逻辑全部堆在CheckoutService里。public class Order { private String orderNo; private OrderStatus status; private final ListOrderItem items new ArrayList(); public void addItem(OrderItem item) { if (status ! OrderStatus.PENDING_PAYMENT) { throw new IllegalStateException(非待支付状态不能加商品); } items.add(item); } public BigDecimal totalAmount() { return items.stream() .map(OrderItem::subtotal) .reduce(BigDecimal.ZERO, BigDecimal::add); } }这段代码的关键在于addItem()里的状态检查它直接把状态图中的约束变成了代码约束只有待支付状态允许追加商品。类图上的方法名addItem和时序图里的消息保持一致status字段对应状态图里的当前状态。代码到图的翻译关系越直接维护成本越低评审也能一眼看出 UML 是真实指导了编码而不是画完就丢。5.3 部署取舍单机收银还是 B/S部署图怎么画部署图不是综合实验的硬性要求但画了能体现对运行环境的理解。超市系统常见两种部署形态单机收银台加本地数据库适合小卖部场景浏览器客户端加应用服务器加数据库适合多门店连锁。部署图里要画出节点、构件和通信路径节点是物理设备构件是运行在节点上的软件单元。部署形态节点组成适用场景建模要点单机 C/S收银终端、MySQL、打印机单门店课程设计强调本地通信数据库与客户端同节点B/S 多门店浏览器、Web 服务器、应用服务器、数据库服务器连锁超市实训强调网络边界网关、内网、数据库分层选择哪种部署形态取决于实验要求。如果是课程设计单机版足够部署图画两个节点即可如果团队项目想体现工程能力B/S 结构加分明显但复杂度也会上升。部署图的作用是逼你想清楚静态模型里设计的服务到底跑在哪台机器上数据库是集中还是本地这些决策反过来会影响包图里基础设施层的设计。6. 模型与代码的一致性检查与工具选择6.1 用脚本对类名让对照表自动生成类图画完、代码写完接下来要做一致性检查。手工对比一张一张图不现实常见做法是用脚本抽取模型文件里的类名集合再和源码目录里的类名集合做差集。PlantUML 导出文本后类名是class或interface关键字后面的标识符一段正则就能抽出来import os, re def extract_model_classes(text): return set(re.findall(r^(?:class|interface)\s(\w), text, re.M)) model_file open(model.puml).read() classes extract_model_classes(model_file) files {f[:-5] for f in os.listdir(src/domain) if f.endswith(.java)} print(缺实现:, classes - files) print(缺模型:, files - classes)脚本逻辑不复杂但强迫类名在两个体系里完全一致。当初建模时用Product写代码时就不要叫Goods一旦名称漂移脚本检查和代码评审都会多花时间。这个脚本不能识别嵌套类、内部类但用来抓主类名已经足够。真正有效的做法是人机结合脚本抓漏人工确认语义对应关系。6.2 UML 工具对比PlantUML、StarUML 与 Visio 怎么选工具选择影响整个建模过程的效率。PlantUML 是文本建模适合放进 Git 仓库每次改动都有 diff 记录团队评审时能看清版本变化StarUML 是图形化工具拖拽快速适合课堂演示和临时探索但模型文件是二进制版本对比困难Visio 的优势在 Office 生态画静态的 uml 类图很快但关系线语义弱生成不了代码也不适合频繁迭代。工具文件形态版本管理适用场景PlantUML纯文本天然适合 Git diff多人协作、需要持续维护模型的实验StarUML二进制工程文件导出图片对比快速画图、课堂演示Visio.vsdx弱仅文件级一次性出图、Office 文档集成我的建议是如果要从分析一路做到代码用 PlantUML因为它把图当成代码资产维护如果只是交一份静态说明文档Visio 或 StarUML 都可以。最怕的是用 Visio 画完几张图后面改需求时重新画一遍图与代码彻底脱节。6.3 让状态图直接约束代码枚举与状态名共用一份定义最后一个技巧把状态图里的状态名变成代码里的枚举常量事件名变成服务方法名两者一一对应。OrderStatus枚举只定义PENDING_PAYMENT、PAID、SHIPPED、RECEIVED、CANCELLED、REFUNDING、REFUNDED服务方法pay()、cancel()、refund()只做状态迁移不做业务计算。状态图里出现但代码里没有的状态就是回归测试的遗漏点代码里出现但状态图里没有的迁移就是评审要提问的地方。把状态机放进一个常量类或枚举里共享前端展示、后端校验、报表统计都从同一份定义取值模型和代码就不再是两份各自漂移的文档。本文还有配套的精品资源点击获取
返回列表