ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

软件工程实践:用例图核心价值与绘制指南

软件工程实践:用例图核心价值与绘制指南 1. 从“画图”到“对话”重新理解用例图的价值很多刚接触软件工程的同学一听到“用例图”第一反应就是“哦就是画几个小人参与者和几个椭圆用例然后用线连起来。” 然后在课程设计或者项目文档里草草画上一张就把它当作一个必须完成但又无足轻重的“形式主义”任务。我见过太多项目用例图要么画得过于简陋要么画得极其复杂但最终都沦为了文档里的一个摆设开发团队几乎没人看测试团队也用不上。这其实完全误解了用例图的核心价值。在我看来用例图根本不是一张简单的“功能清单图”而是一份系统与外部世界的“对话契约”。它的首要目的不是为了给程序员看系统内部怎么实现而是为了在项目早期统一所有干系人客户、产品经理、设计师、开发、测试对系统“做什么”以及“为谁做”的认知。当你画出一个参与者Actor和一个用例Use Case时你实际上是在定义一个清晰的边界系统在这里世界在外面这个特定的外部角色Actor希望通过系统完成这个特定的目标Use Case。这个目标必须是业务层面、用户可感知的价值而不是一个技术操作。举个例子在“在线购物系统”里“用户”这个参与者的目标可能是“提交订单”这是一个完整的、有价值的业务目标。但如果你画成“点击提交按钮”或者“调用订单创建API”那就错了那是实现细节是系统内部行为不应该出现在用例图里。用例图关注的是“What”系统提供什么服务而不是“How”系统内部如何实现。这种视角的转换是画好用例图的第一步也是避免它沦为废纸的关键。为什么现在很多团队包括一些拥抱敏捷的团队又开始重新重视起用例或类似的需求描述方式比如用户故事因为大家发现纯粹的功能列表Feature List或者模糊的自然语言描述极易产生歧义导致后期频繁的需求变更和返工。而一个经过深思熟虑的用例图配合详细的用例规约Use Case Specification能够以一种结构化的方式提前暴露很多需求模糊点和系统边界问题。它迫使你去思考到底有哪些不同类型的用户他们各自的核心目标是什么系统需要为这些目标提供哪些服务这些服务之间有什么关系所以别再把画用例图当成应付差事。接下来我会带你深入拆解用例图的每一个构成元素分享我在实际项目中如何用它来驱动有效的需求讨论以及如何避免那些常见的“坑”。你会发现用好这个工具能在项目初期就建立起一道坚固的“共识防线”。2. 用例图核心元素拆解不止是图形更是语义要画好用例图必须准确理解其每个图形元素背后所代表的精确语义。这就像编程语言的语法用错了表达的意思就全变了。2.1 参与者Actor谁在系统之外“发声”参与者是在系统外部与系统进行交互的人、事物或其他系统。关键在于“外部”和“交互”。识别参与者不是简单罗列所有用户角色而是要找到那些与系统有直接目标交互的外部实体。常见误区与纠正误区一把组织机构当作参与者。比如画一个“财务部”。财务部是一个部门不是直接交互的实体。真正的参与者可能是“财务专员”或“财务经理”。误区二把系统内部组件当作参与者。比如在一个电商系统里画一个“支付网关”作为参与者。支付网关通常是本系统调用的外部系统它确实是参与者。但如果你画一个“订单处理引擎”那它就是系统内部模块不应作为参与者出现。误区三过度细分。比如区分“普通用户”、“VIP用户”、“白银VIP用户”、“黄金VIP用户”。如果这些用户在系统功能交互层面没有本质区别例如都能浏览商品、下单那么就应该合并为“用户”这个泛化参与者。过度细分会导致用例图臃肿失去重点。实操心得识别参与者的一个好方法是问“谁或什么对系统有明确的目标谁从系统提供的服务中获益” 另一个技巧是寻找那些会“启动”Initiate用例的实体。通常参与者位于用例图的边界之外。2.2 用例Use Case系统提供的“价值服务包”用例是系统为参与者提供的、可观测的、有价值的服务单元。它代表一个完整的、从参与者角度出发的目标。用例的命名应该是一个“动词名词”的短语清晰表达目标例如“预约会议室”、“生成月度报表”、“验证用户身份”。命名规范与边界好的命名“办理入住”酒店系统、“发布文章”博客系统、“计算税费”电商系统。这些命名体现了用户完成了一个有业务价值的事情。坏的命名“点击保存按钮”、“打开数据库连接”、“执行算法A”。这些是系统内部的技术动作不是用户目标。粒度把控用例的粒度是艺术。太粗如“管理酒店”没有指导意义太细如“输入姓名”会陷入细节。一个经验法则是一个用例应该代表一个参与者与系统的一次独立“对话”这个对话能达成一个对参与者有意义的结果。通常一个用例的实现对应多个系统操作即多个功能点。2.3 关系Relationships勾勒服务间的逻辑脉络用例图中的关系定义了参与者和用例之间以及用例与用例之间的逻辑联系。正确使用关系是让用例图“活”起来的关键。关联Association连接参与者和用例表示参与者与用例之间存在交互。用一条实线表示。这是最基础、最常用的关系。注意关联线没有方向或可以用箭头表示信息流方向但UML标准中关联是无方向的。实践中为了清晰常用箭头从发起交互的参与者指向用例。包含Include表示一个用例基础用例的行为必然包含另一个用例被包含用例的行为。这是一种强依赖关系类似于子函数调用。用一条带箭头的虚线并标注include。场景多个用例有共同的行为步骤。例如“网上支付”这个用例几乎必然包含“验证支付密码”这个子行为。那么“网上支付”include“验证支付密码”。价值避免重复描述相同的行为流程提高用例规约的复用性和一致性。扩展Extend表示一个用例扩展用例的行为可能有条件地增强另一个用例基础用例的行为。这是一种弱依赖、可选的关系。用一条带箭头的虚线并标注extend箭头指向被扩展的基础用例。场景描述可选的、异常或补充的行为流。例如基础用例是“用户登录”。在“连续登录失败N次”的条件下系统行为会扩展到“锁定用户账户”。那么“锁定用户账户”extend“用户登录”并在扩展点上注明条件。关键区别Include vs. Extend包含是“必须做”扩展是“可能做”。基础用例离开被包含用例是不完整的但离开扩展用例依然是完整的。泛化Generalization表示“是一种”is-a的关系。可以用于参与者之间也可以用于用例之间。参与者泛化例如“管理员”是一种特殊的“用户”。那么“管理员”可以泛化自“用户”。这意味着管理员继承了用户的所有关联用例并可能关联更多用例。用例泛化例如“支付”是一个抽象用例而“信用卡支付”和“支付宝支付”是它的具体化。子用例继承并可能重写或扩展父用例的行为。用例泛化在实际中较少使用因为通常用包含和扩展关系更能清晰地表达可变性。避坑指南新手最容易混淆“包含”和“扩展”。一个简单的判断方法是如果没有被包含用例基础用例的目标还能达成吗如果不能就是包含如没有“验证密码”“支付”无法完成。如果能但某些条件下会有额外行为就是扩展如没有“锁定账户”“登录”本身依然可以完成。3. 实战绘制从零构建一个“图书馆管理系统”用例图理论说再多不如动手画一张。我们以经典的“图书馆管理系统”为例走一遍从需求分析到成图的完整过程。假设我们经过初步沟通得到以下核心需求读者可以查询图书、借阅图书、归还图书、续借图书。图书管理员可以管理图书信息增删改查、管理读者信息、处理借阅和归还。系统管理员可以管理用户账户和系统参数。借书时系统需要检查读者借阅数量是否超限、图书是否可借。还书时如果超期系统需要自动计算罚款。3.1 第一步识别参与者我们根据“外部交互实体”和“有明确目标”的原则来筛选读者Borrower使用系统进行图书查询、借阅、归还的核心用户。这是一个明确的角色。图书管理员Librarian负责处理借还书业务、管理图书和读者信息的后台工作人员。注意他/她与“读者”是不同的角色目标不同。系统管理员System Administrator负责系统维护和基础数据管理。这个角色与具体的图书馆业务借还书是分离的。外部支付系统External Payment System可选如果罚款支持在线支付那么处理支付的外部系统就是一个参与者。这里我们先不考虑以简化模型。初步确认三个主要参与者读者、图书管理员、系统管理员。3.2 第二步识别用例为每个参与者列出其希望通过系统达成的目标动词名词读者Borrower查询图书Search Books借阅图书Borrow Book归还图书Return Book续借图书Renew Book可选查看借阅记录View Loan History可选缴纳罚款Pay Fine//如果考虑在线支付这个用例会关联外部支付系统参与者图书管理员Librarian管理图书信息Manage Book Information//这是一个较粗的用例可以包含增删改查管理读者信息Manage Borrower Information处理图书借阅Process Book Borrowing//注意这是管理员视角的“办理借书手续”与读者的“借阅图书”是同一业务的不同入口处理图书归还Process Book Returning处理续借Process Renewal计算并登记罚款Calculate Record Fine系统管理员System Administrator管理用户账户Manage User Accounts配置系统参数Configure System Parameters查看系统日志View System Logs3.3 第三步提炼关系与优化结构现在我们有了初步的参与者和用例列表。直接连线会得到一张杂乱无章的图。我们需要运用“包含”和“扩展”关系来优化结构体现业务逻辑。分析“借阅图书”流程无论是读者自助借阅还是管理员处理借阅核心流程都包含几个关键检查步骤。我们可以将这些步骤抽象为独立的子用例。include关系无论是Borrow Book读者还是Process Book Borrowing管理员要成功完成都必须执行“检查借阅资格”Check Borrowing Eligibility 包括检查借阅数量上限、是否有超期未还书等和“更新图书状态”Update Book Status 将图书状态改为“已借出”这两个行为。因此Borrow BookincludeCheck Borrowing EligibilityBorrow BookincludeUpdate Book StatusProcess Book BorrowingincludeCheck Borrowing EligibilityProcess Book BorrowingincludeUpdate Book Status这样我们就避免了在两个用例中重复描述相同的检查逻辑。分析“归还图书”流程核心行为是“更新图书状态”将状态改为“可借”。但有一个可选的扩展行为如果超期需要“计算罚款”。include关系Return Book和Process Book Returning都必须includeUpdate Book Status这里复用同一个子用例。extend关系Calculate FineextendReturn Book 扩展点是“当图书归还日期晚于应还日期时”。同样Calculate FineextendProcess Book Returning。处理“管理信息”的粗粒度用例Manage Book Information和Manage Borrower Information太粗可以将其展开为具体的“增加”、“删除”、“修改”、“查询”用例或者保留为粗粒度在详细的用例规约中描述其子流。为了图面清晰初学者可以暂时用粗粒度表示但心中要明白它代表一组操作。参与者泛化考虑“图书管理员”和“系统管理员”是否都是更广义的“管理员”的一种如果他们有一些共同用例比如“登录系统”、“修改个人信息”可以抽象出一个“管理员”父参与者。但在这个例子中他们的业务目标差异较大可以不泛化保持独立更清晰。3.4 第四步绘制与评审根据以上分析我们可以用绘图工具如 draw.io, Lucidchart, 甚至 PowerPoint/Visio或支持 UML 的 IDE 插件来绘制。绘制时注意布局美观将相关的参与者和用例放在一起。绘制完成后这张图就成为了需求讨论的焦点。你应该拿着它去和客户、产品经理、开发团队进行评审问以下问题对参与者“除了读者、图书管理员、系统管理员还有其他人或系统需要和我们交互吗比如是否需要向图书供应商的系统发送采购数据”对用例“查询图书是否包含了按作者、ISBN、主题等多种方式查询‘续借’有没有次数限制这个限制应该在哪里体现”对关系“处理借阅一定要包含检查借阅资格这一点大家是否认同如果检查不通过流程是什么这个异常流我们在用例规约里要写清楚。”对系统边界“计算罚款的规则是什么是由系统自动计算还是管理员手动输入计算罚款这个用例的细节需要单独规约。”通过这样的评审很多隐藏的需求和规则就会被挖掘出来。用例图就像一个“地图”引导大家进行系统性的探索而不是漫无目的地讨论。4. 从用例图到用例规约让静态图形驱动动态开发画出一张清晰的用例图只是完成了需求建模的第一步。用例图定义了系统的功能范围和静态关系但每个用例具体如何执行有哪些步骤有哪些异常情况这就是用例规约Use Case Specification的任务。两者结合才能构成完整的需求描述。很多人只画图不写规约导致用例图的价值大打折扣。一个标准的用例规约通常包含以下内容用例名称与用例图中的名称一致。参与者主要参与者和其他相关参与者。前置条件执行此用例前系统必须满足的状态。例如“借阅图书”的前置条件可能是“用户已成功登录系统”、“图书存在于馆藏中且状态为‘可借’”。后置条件用例成功执行后系统状态发生的变化。例如“借阅图书”成功的后置条件可能是“该图书的状态被更新为‘已借出’”、“用户的借阅记录中增加一条信息”。基本事件流描述用例最典型、最顺利的执行路径。用编号步骤列出参与者和系统的交互。1. 读者输入查询条件请求查询图书。 2. 系统显示匹配的图书列表。 3. 读者选择一本可借的图书点击“借阅”。 4. 系统执行“检查借阅资格”子流程。 5. 系统检查通过执行“更新图书状态”子流程。 6. 系统记录借阅信息提示借阅成功。扩展事件流描述基本流中可能出现的分支、异常或可选情况。通常对应图中的extend关系或包含关系的异常处理。4a. 检查借阅资格失败如借书量已达上限 4a1. 系统提示读者“借书量已达上限无法借阅”。 4a2. 用例结束。 5a. 更新图书状态失败如并发操作导致图书已被借走 5a1. 系统提示读者“图书状态已变化请刷新重试”。 5a2. 用例结束。特殊需求非功能需求如性能要求“查询响应时间小于2秒”、安全性要求“借阅请求需通过身份验证”。实操心得编写用例规约的“节奏感”不要试图一次性写完所有用例的所有细节。我推荐采用“分层细化、迭代编写”的方式第一轮蓝图期在绘制用例图的同时为每个核心用例特别是与主要参与者相关的写下简短的基本事件流3-5步和关键的扩展事件流1-2个最可能发生的异常。目的是快速确认核心业务流程是否被所有人理解。第二轮需求深化期在项目启动或迭代计划开始前针对本次迭代要实现的用例详细编写完整的规约包括详细的前置/后置条件、所有可能的扩展流。这是开发人员和测试人员编写设计用例和测试用例的直接输入。第三轮开发测试期在开发和测试过程中随着理解的深入会发现规约中模糊或遗漏的地方。及时更新用例规约并将其作为最终验收的依据。用例规约应该是一个“活的”文档。将用例图和详细的用例规约放入需求管理工具如Confluence, Wiki或项目管理系统让它们成为团队共享的、唯一的需求源。开发任务可以从用例规约中拆分测试用例可以直接验证事件流中的每一步。这样用例就从一张孤立的图变成了连接需求、设计、开发、测试的活纽带。5. 高级话题与常见陷阱超越入门指南掌握了基础绘制和规约编写你已经能应对大部分场景。但在复杂系统或特定方法论中还会遇到一些进阶问题和陷阱。5.1 系统边界与子系统表示用例图的边界框代表了待开发的系统。对于大型系统可以分层绘制用例图。顶层用例图展示整个系统与所有外部参与者的交互。系统就是一个黑盒子。子系统用例图将系统拆分为若干子系统每个子系统有自己的用例图。此时其他子系统或本系统的其他部分可能成为当前子系统的“参与者”。例如在“图书馆管理系统”中可以拆出“借还书管理子系统”、“图书编目子系统”、“用户管理子系统”。“借还书管理子系统”的用例图中“用户管理子系统”可能作为一个参与者为其提供“验证用户身份”的服务。注意这种内部子系统间的交互在最初的业务需求层面可能不体现是在系统架构设计阶段引入的。要谨慎使用避免过早陷入内部设计混淆了业务需求边界。5.2 用例图的“度”如何避免过度设计或设计不足设计不足过于抽象整个系统只有3个参与者和5个用例。例如只有“用户”、“管理员”和“管理数据”、“处理业务”两个用例。这种图几乎没有信息量无法指导任何后续工作。过度设计过于具体试图用用例图描述所有业务规则和操作细节。例如把“输入用户名”、“输入密码”、“点击登录按钮”都画成独立的用例并用复杂的关系网连接。这混淆了需求分析做什么和系统设计怎么做的层次。黄金法则用例图的粒度应以能清晰界定系统范围、并足以作为编写用例规约的目录为准。如果一个“用例”无法写出一个独立、完整的事件流哪怕只有基本流那它可能就不是一个恰当的用例或许只是某个用例中的一个步骤。5.3 用例图与敏捷开发中的用户故事User Story很多人问敏捷开发中我们写用户故事As a ... I want to ... So that ...还需要用例图吗它们并不冲突而是可以互补。用户故事更轻量侧重于从用户角度描述一个有价值的功能点是规划和沟通的绝佳工具。它擅长捕捉零散的需求和进行优先级排序。用例图/规约更结构化侧重于完整地描述一个交互场景的所有可能路径基本流、扩展流是详细需求说明和测试设计的绝佳工具。它擅长描述复杂的、有多个分支的业务流程。我的实践在大型或业务逻辑复杂的敏捷项目中我经常这样结合使用用用户故事墙进行产品待办列表Product Backlog的管理和迭代规划。对于核心、复杂的史诗Epic或大型故事在迭代开始前的细化Backlog Refinement阶段用用例图来梳理该功能涉及的所有参与者和交互场景确保没有遗漏。在迭代中针对每个要实现的用户故事如果需要特别是涉及复杂业务逻辑的为其编写简明的用例规约聚焦于基本流和关键异常流作为开发者和测试者共同理解的“验收标准”细节。这个规约可以就写在用户故事卡的背面或Confluence页面里。5.4 工具选择与团队协作手绘草图用于快速讨论非常好但最终需要电子版进行维护和共享。在线协作工具draw.io(diagrams.net)、Lucidchart、Miro。这些工具支持实时协作非常适合团队远程进行需求梳理和用例图绘制。它们通常也提供UML图形库。专业建模工具Enterprise Architect,Visual Paradigm。功能强大支持完整的UML模型和代码工程适合大型复杂系统或严格遵循模型驱动开发MDD的团队。集成在IDE中的工具如Visual Studio的类设计器、IntelliJ IDEA的UML插件。方便开发人员在设计时直接查看和修改模型。团队协作关键无论用什么工具最重要的是让用例图成为“活的”共识载体而不是一份画完就锁进文档库的“死”资产。定期在迭代评审会或需求讨论会上回顾和更新它。
返回列表