1. 先从“扩展特征”到底是什么说起
用例图上的箭头,大概是最容易画错的东西。尤其是那个带<<extend>>的虚线箭头——形式上学两天UML就会画,搁到真实项目里一用,十有八九会把它和包含关系、继承关系搅在一起,最后整个用例模型画得跟蜘蛛网似的,评审会上谁看了都摇头。
先别急着翻规范和速查表,我试着用大白话把“扩展特征”讲清楚。
所谓扩展特征,说白了就是:主流程本来能正常走完,但在某个特定节点上,可能会出现一些“可做可不做”“做了会改变走向”的额外动作。这些额外动作如果直接揉进主流程,会把主流程搞得臃肿不堪;如果单独拎出来,又必须说清楚它挂在哪个位置、什么条件下触发、触发之后对主流程有什么影响。这个“单独拎出来、挂在特定位置、有条件触发”的东西,就是扩展特征。
举个最常见的例子,你做一个在线商城的下单功能。用户选好商品,填写地址,提交订单,支付成功——这是主流程。但支付环节里,用户突然发现优惠券过期了,系统弹出来一个提示,问你要不要放弃使用这张券?这就是一个典型的扩展场景。它没有改变“提交订单→支付”的主路径,只是在支付这个节点上,出现了一个“可选的处理动作”。你再想想,如果把这个“优惠券过期处理”直接塞进主流程,那主流程的用例描述会被细节淹没,测试人员看着那十几页的异常分支只想离职。
这里又得说清楚一个概念边界:扩展特征和包含关系、泛化关系长得像,干的事情完全不一样。包含关系是“我不管走哪条路,都必定会做这一步”,比如任何订单支付之前都必然要调用风控接口,那是强制性的、内置的;泛化关系是“一个用例是另一个用例的变体”,比如“普通支付”和“积分抵扣支付”都是“支付”的变体,父用例的骨架被子用例继承;而扩展关系是“主流程满足了某个条件之后,额外插入一段行为,这段行为在正常情况下根本不会出现”。这个边界,是理解扩展特征的第一道门槛。
所以,标题里那个“定义”二字,其实才是真正的核心。画一个箭头容易,把扩展特征定义得清晰、可落地、可评审、可测试,才是需求分析里真正考验功力的事情。后面我会从判断标准、识别方法、实操流程、常见翻车现场四个维度把这件事彻底拆开,保证你看完能直接拿回自己的项目里去用。
2. 先建立正确的直觉框架:什么时候该用扩展特征
2.1 扩展关系背后的“触发哲学”
在动手建模之前,得先建立一个稳定的直觉框架。我在实际项目中反复验证下来,最能帮助团队统一认知的,是下面这条判断逻辑:
一个行为应不应该建模成扩展特征,就看它是不是“在主流程之外的某个既定接缝处,被一个非必然条件触发的可选动作”。
这个判断里包含三个关键词:接缝、非必然条件、可选动作。缺一个都不能算扩展。
“接缝”强调的是位置感。一个扩展行为必须有明确的挂载点,这个挂载点通常叫“扩展点”。比如订单支付用例里,“支付方式选择”和“确认支付”中间,就是一个接缝——优惠券过期后的提示和引导,恰好挂在这个接缝上。扩展点定义得越精确,后续实现方越清楚在哪个环节做拦截。
“非必然条件”强调的则是触发感。如果某个动作是每次执行主流程时必然发生的,那它属于包含或步骤本身,不属于扩展。比如支付前检查库存,每次支付之前都要查,这不能叫扩展;但“库存不足时跳出提示并建议相似商品”,只在库存不足时才出现,这就是扩展。条件的存在,是扩展区别于普通步骤的试金石。
“可选动作”则决定了类型。这里要注意,扩展动作虽然可选,但不能是“画蛇添足”的日志记录或埋点上报——虽然在实现层面它们确实是可选的,但从业务价值角度,这类系统级动作不承载用户可感知的业务行为。扩展特征应当承载一个有业务含义的、可以被用户或系统感知的独立行为片段。
2.2 判断扩展关系的三道送分题
我总结了几个自测问题,每次拿不准的时候就用它过一遍,准确率非常高。问法很简单,一共三问:
第一问:把这段行为从主流程里拿掉,主流程是否仍然完整、可用?如果答案是“依然完整”,那么它有可能是扩展。如果拿掉之后主流程根本走不通,那它不是扩展,它是主流程的固有步骤。
第二问:这段行为的发生是否必须满足某个前置条件?如果没有任何条件,一百次里九十九次都要做,那它有可能是包含而不是扩展。扩展的前提永远是“这个条件不一定为真”。
第三问:这段行为发生时,是否影响主流程的后续走向?这是一个很容易被忽略的特征。扩展不仅仅是在主流程里插入一段动作,它有可能改变主流程的输出结果。比如库存不足的提示,如果用户接受推荐,可能跳转到商品详情去换一个商品,原订单流程被中断。如果对主流程走向完全没有影响,那它更像是一个附属提醒,未必值得单独建模成扩展用例。
这三问过完之后,如果你拿到的三个答案分别是“是、是、是”——那别犹豫,它就是扩展特征。
2.3 一套判断扩展关系的快速决策流程
实际评审过程中,光靠“感觉”是不行的,团队里每个人对“可选”的理解可能有细微偏差。我习惯把上面三连问做成一小段决策流程,让每位参与评审的人依序回答:
- 先问主流程在去掉该行为后,是否仍然能正常完成核心目标。能,则继续;不能,则判定为固有步骤。
- 再问触发该行为的条件是否是“特定情况下才有”,比如超时、异常、特殊用户类型、特殊业务规则。是,则继续;否,回到包含关系重新评估。
- 最后问该行为出现时,用户能感知到的业务结果是否不同于默认主流程的结果。若结果是不同的,则可以建模为扩展用例。
只要完整走一遍这套流程,80%的场景都能得到清晰结论。剩下的20%,就是那些业务边界设计得模棱两可的项目,需要产品经理拍板说清楚业务规则后,再回过来补判。这套流程我也做成过一个表格给团队使用,判断速度提升了一大截。
3. 识别与定义扩展特征,需要想清楚的几件事
3.1 哪些场景最容易隐藏扩展特征
知道判断标准之后,还有一个更现实的问题:去哪里找扩展特征?经验不足的分析师最常犯的错误是把注意力全放在正常流程的完善度上,结果扩展特征被漏得干干净净。其实,扩展特征最喜欢藏在下面这几类场景里:
各种“超限”类场景。比如库存超卖、金额超限、时间超时、次数超频。系统在运行到大额交易、热门秒杀这类极限场景时,就很容易冒出新的可选行为。
各种“特殊身份”类场景。同一个操作,普通用户走主流程,VIP用户可能额外有免排队资格,内部测试账号可能跳过某些校验,青少年账号可能收到额外的内容过滤提示。这些因身份不同而新增的行为,在用例建模时通常就是扩展特征。
各种“中断与恢复”类场景。比如下载大文件时网络中断,重新连接后是续传还是重新开始?支付时app被杀,重新打开是回到订单页还是直接跳转支付结果?这些发生在异常边界上的处理,天然具备“可选、条件触发”的属性。
各种“业务规则切换”类场景。比如结算时判断用户是否有满减券可叠加、下单时判断收货地址是否超出配送范围、申请请假时判断是否命中法定节假日。这一类在电商、ERP、办公审批系统里多得数不过来。
每次做需求梳理时,我建议你专门拿出一张纸,把这五类场景全部列出来,逐个对着主流程过一遍,漏检率会低很多。
3.2 扩展特征必须说清楚的四个字段
判断出了扩展特征,不等于定义完成。真正的挑战在于把一个模糊的“可选动作”变成可开发、可测试的完整描述。我一般要求任何一条扩展特征必须填满以下四个字段:
触发条件:什么情况下,系统才会进入扩展流程?注意这里要写可观测、可判定的条件,不能是语义模糊的“必要时”“酌情”。比如“当用户使用优惠券且该券已过期”就比“优惠券异常时”清晰得多。
扩展点位置:扩展行为插在主流程的哪个阶段?这里要精确到主流程的某个步骤索引或某个状态节点。比如“在支付确认弹窗弹出前”“在库存校验通过后、生成订单前”。扩展点写不清楚,程序员就会拿着需求来来回回找你确认。
扩展流程:一旦触发,系统按什么顺序执行哪些动作?这里要写成可逐步验证的操作序列,而不是一句笼统的描述。比如第一步展示过期提示文案,第二步按钮切换为“不使用优惠券”,第三步用户点击后回到原支付流程。
对主流程的影响:扩展流程执行完毕后,主流程是被中断、回退到扩展点之前的某一步,还是继续执行?这个影响关系必须显性地写出来。很多人画用例图时不注意这个,导致后续写测试用例时根本不知道该验证什么结果。
这四个字段填完之后,扩展特征就不再是一个概念性的判断,而是一条可落地的工作指令。后面我要讲的实操案例,也会严格按这四个字段来组织和展示。
3.3 警惕“伪扩展”:这些误判最容易把人带沟里
最后一个需要前置观念先讲清楚的,是识别“伪扩展”。有时候一个行为看起来很像扩展,但本质上根本不是,如果直接建模成扩展,反而会让模型失去解释力。
第一种常见伪扩展是“必做校验”。很多新手看到“如果手机号未绑定,则弹窗引导绑定”就觉得这是扩展。但要是这是一个新用户注册后首次下单必走的校验流程,那它就是主流程的一部分,不是扩展。区分方法很简单:把那个“如果”变成“无论什么情况都必须执行”,看看业务上是否成立。成立,则归入主流程;不成立,才是扩展。
第二种伪扩展叫“状态分支”。比如物流单当前状态是“已签收”时显示签收图,是“运输中”时显示动效图。这看着确实是“可选”,但它们是同一个展示组件在不同状态下的表现,不是一个额外插入的独立行为序列。这种情况适合建模为状态机,而不是扩展用例。
第三种伪扩展出现在“补充式文案展示”这类场景。比如登录时如果用户来自某个渠道,页面底部多显示一行推广语。这类行为虽然符合可选条件触发,但它不构成完整的行为序列,不具备独立的业务价值闭环。把它做成扩展用例,只会白白增加模型的噪音。
记住一句话:扩展特征是用来承载“一个独立的可选行为片段”的,不是用来装所有条件分支的。这个边界守住,你的用例模型就会非常干净,评审会也会轻松很多。
4. 实操练习:一个完整案例手把手走一遍
4.1 业务背景与初始用例
纸上谈兵差不多了,我拿一个实际做过的需求场景来完整走一遍。背景是一个在线医疗问诊平台的“在线复诊”功能。主流程是这样的:
- 患者选择历史就诊记录
- 系统校验该记录是否支持在线复诊
- 患者描述当前症状并上传图片
- 系统匹配医生并生成问诊单
- 患者支付问诊费用
- 医生接诊并开始图文问诊
画用例图的时候,大部分人会把上面六步做成一个用例“在线复诊”,然后呢?然后就没有然后了——因为扩展特征经常藏在那些没有被显性描述出来的边界里。
我带着团队做了一次头脑风暴,要求大家只思考一个问题:哪些情况下,这个主流程会往外“冒分支”?结果一口气找出来七八个。我当时心里就知道,这个用例绝不是一张图加一段文字能搞定的。
4.2 逐步判断,找出真正的扩展特征
每找到一个候选分支,我们就像前面说的那样,用那三连问一一过堂。
第一个候选:复诊资格过期处理。系统校验时发现历史就诊记录距今已超过一年,平台规则要求超过一年必须重新初诊,不能直接复诊。去掉这个行为后,主流程依然完整;触发条件是非必然的,只有超过一年才会出现;扩展流程执行后,对主流程的影响是中断,并引导用户转初诊。三个答案全中,斩钉截铁,这就是典型的扩展特征。
第二个候选:症状图片上传失败的容错处理。图片超过大小限制,或格式不支持,系统给出提示并引导患者重新上传。去掉它,主流程也能走(只要图片正常);但等等——图片上传是不是一个必然环节?这里就要细分:如果这次问诊“必传图片”,那么上传失败时的提示虽然不属于主流程,但它确实只在上传这一步的异常分支上发生。所以依然可以建模成扩展特征,扩展点就在“上传图片”这一步,触发条件是文件校验失败。
第三个候选:医生离线时的通知机制。用户提交问诊单以后,发现匹配到的医生当前离线,系统通过手机通知医生,医生上线后再次提醒。这个行为对患者的主流程没有直接影响,更多是后台的任务调度。它适合放在系统内部的异步流程中,不适合作为一个患者视角的扩展用例。最终我们没有把它建模成扩展特征,而是作为系统需求在技术设计环节单独描述。
第四个候选:支付超时取消问诊单。患者提交问诊单后,超过30分钟未支付,系统自动取消问诊单并释放医生资源。这看起来很像是“支付”主流程的一个可选分支。但仔细判断,它的触发条件实际上是“用户不进行操作”,在用户完全不参与的情况下系统自动完成,这也是一个特殊形态的扩展行为——触发者是时间,而非用户操作。我们最终仍然保留了它,但在扩展流程里明确写了“由定时任务触发”,并补充了支付结果回调接口的处理逻辑。
这样一个一个过滤下来,原本看似简单的“在线复诊”用例,最终梳理出了3个需要纳入用例图表达的扩展特征。每一个都对应一段需要在产品需求文档中充分展开描述的规则。
4.3 从识别到定义:写清一条扩展特征的全过程
拿第一个“复诊资格过期处理”来展示完整的定义过程。
第一步,确定扩展点。主流程步骤编号是第2步“系统校验历史就诊记录支持在线复诊”,扩展点位置我们就定位在这一个环节。这里有一个很重要的细节:扩展点必须写到“哪一个动作之后、哪一个动作之前”,比“校验环节”更精确。因为实际的代码实现中,校验是一个方法,你告诉程序员扩展发生在“校验成功之后、生成问诊单之前”,他就能精确地在正确的位置挂上钩子。我们最终写的是“校验通过与否的结果返回后,在进入下一步‘患者描述症状’前”。
第二步,写清楚触发条件。我们最终的描述是“当患者选择的历史就诊记录距离最后一次就诊时间超过365天,且该记录对应的科室不在复诊白名单内”。也许有人会觉得要写两个条件,太啰嗦,但真实业务里确实存在特殊情况——某些科室哪怕超过一年也允许复诊。把条件写全,开发才不会到你面前追问“要是这个科室比较特殊怎么办”。
第三步,定义扩展流程。这里要写成一个可执行的操作序列:系统向患者展示提示页,文案说明“该就诊记录已超过在线复诊时限,请重新挂号初诊”,页面提供跳转按钮“去初诊”,同时保留返回按钮“重新选择记录”;若用户选择“去初诊”,跳转到初诊流程并携带原就诊记录ID以便复用病史信息;若选择“重新选择记录”,则回到主流程第1步。注意,这里不仅是给用户看画面,还定义了数据传递规则,开发、测试都能顺着这段话写出具体case。
第四步,明确对主流程的影响。这个扩展特征的影响不是“继续主流程”,而是“中断主流程并跳转到另一个用例”。我们还要补充一个细节:跳转到初诊流程后,如果用户在初诊流程里取消操作,应该返回哪里?我们把“取消后返回原复诊用例的第一步骤”也写进了影响描述里。这个边界补充完成之后,这条扩展特征才算真正闭环。
5. 踩坑实录:扩展特征定义中的高频翻车现场
5.1 扩展点写得太粗,开发反复来问
最常见的问题,是扩展点位置写得太模糊。我的项目里曾经有人写“支付环节”,结果开发问:到底是在点击支付前、调起收银台后、还是支付回调处理后?三个地方代码都叫支付环节,但挂载方式完全不同。后来我强制要求所有扩展点必须用“主流程步骤编号 + 动作名称 + 前后界标”三者联合定位,比如“步骤3.2上传图片动作完成后,在步骤3.3提交问诊单动作前”。这个习惯养成之后,需求澄清会议明显变短了。
5.2 把处理流程写成了报错文案
另一个高频问题,是把扩展流程写得像前端文案一样,只有“提示‘网络异常,请稍后再试’”,而没有讲清楚提示之后系统做什么、用户有哪些操作选项、操作之后的状态变化是什么。我在评审会上经常说一句话:提示文案只是扩展流程的冰山一角,水面下那一大坨状态流转才是关键。如果一条扩展特征只写了文案,完全不写数据如何流转、状态如何迁移,这条定义基本等于废的。
5.3 触发条件里的“等”字灾难
还有一次,一个产品经理写“当用户身份异常时触发”,评审的时候所有人都在问:什么叫身份异常?是token过期、是账号被封禁、还是实名认证信息不匹配?他解释说是“就是异常了嘛”,被测试负责人当场要求写完才能进入迭代。我后来定了一条规矩:任何扩展特征的触发条件里不允许出现“等”“其他”“特殊情况”这类含混词汇,必须枚举出你可以想象到的所有具体条件。如果确实存在无法穷举的情况,必须增加“兜底扩展流程”来覆盖未知条件。
5.4 扩展特征无限膨胀
最后一个大坑,是过度使用扩展。如果一个用例挂上七八个扩展箭头,那大概率不是扩展定义得好,而是主流程本身被拆错了。遇上这种情况,我会退回一步,看看是不是把一些本该属于前置条件、系统约束、外部接口协议的内容也混了进来。通常砍掉一半“伪扩展”,模型就干净了。你在画图时如果有一种“这个用例被咬得千疮百孔”的直觉,那八成是识别跑偏了。
说到底,扩展特征从来不是一个画图技巧,而是一种思维方式。它逼着你在设计每个用例的时候,不止想清楚阳光大道,还要想清楚岔路、旁路、断头路分别是什么样。想清楚这些,用例才能从“流程示意图”变成真正的需求契约。