软件分析与设计笔记 27:Paper 1 应试:从读案例到交卷的一次完整演算

拿一份完整的案例,从头到尾走一遍:怎么读案例列角色、怎么在四个小问之间分配时间、每一张图从哪一句话推出来、两张表怎么填、交卷前拿什么清单自查。附 C25 真题与样卷的题型分值对照。

学习#应试#软件分析#软件设计#学习笔记

本文是 《软件分析与设计笔记》 系列的第 27 篇,共 28 篇

这是我在准备 Software Analysis and Design(NUS-ISS,Paper 1 / SA4101)考试时整理的应试笔记。卷面信息全部来自课程放出的 C25 真题与 C25/C26 样卷,图纸规范来自课程讲义与 workshop 的批注。概念部分我按自己的理解写,示例是自己编的。理解有偏差的地方欢迎指出。

配套阅读:这一篇讲「拿到卷子怎么走完全程」;符号在 draw.io 里具体怎么落笔,见笔记 28。两篇的分工是:这里管判断与顺序,那里管落笔与自检。

0 开卷考试最反直觉的一点

第一次听到「开卷、可以用计算器、可以带纸质资料」,很容易得出一个结论:那就不用背了。

真正考过一场模拟之后会发现,开卷改变的不是「要不要记」,而是 「时间花在哪」:

  • 3 小时里前 30 分钟是阅读时间,不许动笔,实际答题只有 150 分钟;
  • 60 分的卷子里,画图题的合计在 38–50 分之间(用例图、活动图、界面草图、顺序图、类图、重构后重画的类图),画图是没法「现场查着学」的;
  • 能查的是术语、符号含义、规范化规则 —— 但这些恰恰是翻书最快能找到、却最占时间的东西。

所以开卷的真正策略是:把「查得到的东西」提前压成一页索引,把省下的时间全投到画图和读案例上。

1 卷面结构

1.1 总体框架

三个数字先记住:60 分、2 个 Section、150 分钟净答题时间。 Sections 之间分数对称,各 30 分。

Section ASection B
分3030
组织方式一个长案例,串起 4–5 个小问2–3 个独立小题
案例来源一份 1.5 页左右的业务叙述一份产品说明 + 用例描述,或一段代码
主要考什么需求侧产出:用例、活动图、界面、测试设计侧产出:顺序图、类图、坏味道与重构
对应笔记05、08、10、12、14、1521、22、24、25

Section A 的案例读起来很长,但每一个小问只对应案例里的一两句话。这一点在 §2 会用一张图说清楚。

1.2 题型与分值的两年对照

把 C25 真题和 C25/C26 样卷并排放,重复出现的题型就一目了然了:

题型C25 真题C25/C26 样卷分值
数据驱动分析表(Descriptive / Diagnostic / Predictive / Prescriptive)1a(6)—6
用例图1b(8)1a(10)8–10
活动图(要求含实体)1c(8)1b(8)8
界面 / 仪表板草图(要求用隐喻)1d(4)1c(4)4
测试用例 + 测试数据1e(4)—4
顺序图(analysis model)2(12)2a(10)10–12
类图(只画类与关系)4b(6)2b(10)6–10
坏味道识别 + 重构方案4a(6)3(10,含改设计)6–10
用多态满足「可插拔」的非功能需求3(6)—6
精炼用例图(include / extend / 泛化)—1d(8)8

两张卷子的总分都是 60,但小问的切法不一样 —— 这就是为什么不能只按样卷的题型准备:

  • C25 真题 Section A 五问(6 + 8 + 8 + 4 + 4),Section B 三问(12 + 6 + 12);
  • 样卷 Section A 四问(10 + 8 + 4 + 8),Section B 三问(10 + 10 + 10)。

三个判断:

  1. 用例图、活动图、顺序图、类图这四项是硬通货,任何一张卷子上都会出现,合计 32–40 分;
  2. 样卷会换题。C25 真题把样卷的「精炼用例图」换成了「数据驱动分析表」;
  3. 分数最少的题(4 分)反而最容易被写飞 —— 界面草图只有 4 分,但它要求你同时证明「信息选对了」和「隐喻用对了」两件事。

1.3 150 分钟怎么切

Paper 1 的三小时考场时间轴:0 到 30 分钟阅读时间用于读案例并标出角色与痛点,30 到 100 分钟做 Section A 的用例图、活动图与界面草图,100 到 170 分钟做 Section B 的顺序图、类图与重构题,最后 10 分钟回查箭头方向、系统边界与假设声明

这张图是我给自己定的分配,原则有两条:画图题优先,能查书的题垫后;每张图留出 5 分钟检查符号。

时段干什么为什么这样排
0–30 min(阅读)通读两份卷子 → 在案例上标出所有角色、痛点、外部系统不许动笔,但可以在卷子上做标记;这半小时决定了后面所有答案的完整度
30–100 minSection A 的画图题(用例图 → 活动图 → 界面)这些是「想清楚才能画」的题,越到后面脑子越乱
100–170 minSection B(顺序图 → 类图 → 重构)顺序图要照着 basic flow 一步步推,需要连续时间
170–180 min回查:箭头方向、系统边界、«include»/«extend» 有没有画反这 10 分钟通常能捞回 2–4 分,是全卷性价比最高的一段

一个反例:先做「坏味道 + 重构」这种文字题,因为它看起来最容易动笔。结果是写了 20 分钟的文字,回过头发现顺序图只剩 15 分钟 —— 文字题写多了不会多给分,图没画完一定扣分。

2 演算用的案例

从这一节开始,我用一份自己编的案例把整份卷子走完。它刻意做成了真题的形态:一个长叙述,四个小问挂上去。

派单宝 —— 家电维修上门服务平台

某家电连锁有 6 个片区、约 40 名维修工程师。现在的报修流程全靠电话:客户打客服热线 → 客服在 Excel 里记下地址和故障 → 打电话找有空的工程师 → 再回电话告诉客户几点上门。

客服小林每天要接 80 多通电话。她最头疼的是:不知道哪个工程师现在有空,只能一个个打过去问;客户问「能不能明天上午」,她也答不上来。她还在纸上记「谁欠了哪个配件」,经常漏。

工程师老陈一天跑 4–5 单。他出发前不知道客户家的机器型号和故障描述,到了现场才发现要用的配件没带,只能再跑一趟。他也看不到自己这周的排期,客服临时塞单会打乱他的路线。

仓管小周管着片区仓库。工程师来领料时她才知道要出什么,经常出现「工程师到了、配件没了」的情况。她也说不清哪些配件快用完了。

片区经理王姐要向总部报月度数据: completion rate、平均上门时长、客户满意度。她现在靠月底手工汇总 Excel,往往要两三天,且数字对不上。

还有一方:配件供应商有开放的库存查询接口,能实时返回某个配件的可售数量。

王姐希望系统能自动派单、让客户能看到工程师到哪儿了,并且提前预判哪些配件下周会短缺。

先把这份案例读成一张图。下面是它拆出来的四个层次 —— 这正是后面四道小问各自要用的材料:

案例拆解图:上方是案例原文里的三栏信息(五类角色、三个痛点、一个外部系统),下方是它们各自对应到哪一道小问的输出物——角色与目标对应用例图,工程师的故障与配件问题对应活动图,客服的排期痛点对应界面草图,经理的缺料预判对应数据驱动分析表

这张图的意思是:不要逐句精读,要按「谁、什么事、想要什么」三栏去扫。扫完你就会发现,案例里几乎每一句话都能落到某一道题上;落不上的句子,多半是背景交代。

3 第 1a 题:数据驱动分析表(6 分)

题目要求:针对案例里「不知道哪些配件下周会短缺」这个痛点,用 Descriptive–Diagnostic–Predictive–Prescriptive 四层给出分析方案。

题目考的其实不是分析技术,而是能不能把一句抱怨翻成「要什么数据、为什么」。

3.1 答题步骤

  1. 先把痛点原句抄下来(就一句),写在表格上方当锚点 —— 「说不清哪些配件快用完,只能等工程师到了才发现没货」;
  2. 逐层回答三个固定问题:需要什么数据?用什么分析手段或工具?对业务有什么用?;
  3. 每一格都要落到这个案例的业务名词上。「预测」不能写成「用 AI 预测」,要写成「用近 12 个月的配件出库记录,预测下周各片区的配件需求,供仓管提前备货」。

四层分析递进图:从下往上依次是描述性、诊断性、预测性、处方性。每层标出它回答的问题、需要的数据、分析手段以及对业务的作用;右侧用一条上升的阶梯表示难度与价值递增。图上用红字特别标出处方性必须落到「系统替人做的建议或提醒」,否则与前一层没有区别

四层的分工,一句话版:Descriptive 看清发生了什么,Diagnostic 找出为什么,Predictive 猜下一步,Prescriptive 直接给出该做什么动作。

3.2 成品答案

把四层落到本例的具体内容上,就是这张表:

类型需要的数据分析手段业务目标
Descriptive
发生了什么
近 12 个月每张工单的配件消耗记录、每次领料被拒(缺货)的记录按配件 × 片区汇总出库量;统计缺货发生次数看清哪些配件经常缺、缺在哪个片区
Diagnostic
为什么发生
缺货记录 + 当时的库存快照 + 工单故障类型交叉分析:缺货是否集中在某几类故障、是否与工程师领料习惯有关找到根因,避免下次同样缺货
Predictive
可能发生什么
上述历史消耗 + 季节性因素(如夏季空调维修高峰)+ 供应商交期按配件 × 片区的时序预测,给出未来 2 周的预计需求量提前知道下周哪些配件会缺
Prescriptive
该做什么
预测结果 + 当前库存 + 供应商可售库存自动比对,低于安全库存就生成补货建议并推送仓管让仓管在缺货前就下单,而不是等到没货

3.3 两个最容易丢分的地方

  • 四层写成同一件事的四种说法。比如 Predictive 和 Prescriptive 都写「预测风险」——区别在有没有落到动作。Prescriptive 必须产生一个「系统替人做的建议或提醒」,本例就是「自动生成补货建议并推送」;
  • 只写数据不写用途。表格里「业务目标」那一列空着,等于只答了一半。

还有一个隐含加分点:本例有一个外部系统(供应商库存接口)。在 Prescriptive 那一行提到「比对供应商可售库存」,说明你读到了案例里的这一句,而不只是套模板。

4 第 1b 题:用例图(8–10 分)

题目要求:画出支持全部业务角色的完整用例图。

这是全卷分值最高的单张图,也是「完整」两个字最贵的一张。

4.1 答题步骤

  1. 先列角色清单。 从案例里把每一个「有自己目标的人或外部系统」抄出来。判断依据是目标而不是身份(笔记 08);
  2. 判断哪些是外部系统。 本例的「配件供应商库存接口」是一个 actor —— 系统要去查它;
  3. 给每个角色配 2–4 个用例,用例名用动词 + 名词,不用「管理」这种糊词;
  4. 画系统边界框,把所有用例包进去,actor 一律在外;
  5. 连关联线,注意箭头方向;
  6. 补一句假设。 题目要求「如有不清楚处写明假设」,写两行就能拿到稳定性分。

4.2 本例的角色清单

先做一张表,这张表就是图的骨架:

角色他要达成什么对应的用例
客户 Customer报修、知道工程师几点到Report Fault、Track Engineer Arrival、Rate Service
客服 Clerk建单、派单、改约Create Work Order、Assign Engineer、Reschedule Visit
工程师 Engineer接单、现场回填、领料Accept Assignment、Update Work Order、Request Parts
仓管 Storekeeper出库、补货Issue Parts、Raise Replenishment
片区经理 Manager看数据View Dispatch Dashboard、View Parts Forecast
供应商系统 Supplier System被查询库存Query Supplier Stock

这张表里有一处需要专门判断:客服与片区经理能不能共用同一个用例?

他们的「看待派单」「看排期」「改约」完全一样(点进去看到同一个界面、走同一套步骤),只是经理多一个看报表的入口。按讲义的标准 —— both will trigger and execute the use case in the same way —— 可以共用一个用例,画两条线(或者做参与者泛化)。

反过来,「客服派单」与「工程师接单」不能共用:一个发起、一个响应,动作方向都不同。虽然名字都跟「派单」有关,但必须画成两个用例。

判据一句话:这两个角色点进去之后,看到的是同一个界面、走的是同一套步骤吗?是 → 一个用例连两根线;不是 → 拆成两个,哪怕名字听起来像。

4.3 成品图

用例图成品:虚线矩形系统边界内十五个用例,按角色分成五组由上到下排列。边界外左侧自上而下是客户、客服、工程师、仓管、片区经理五个参与者,每人只与同高度的用例组相连,所以连线短且不交叉。右下是橙色的「每日排期推送」用例,用一条橙色箭头指向工程师,表示由系统主动触发;再往右下、边界之外是配件供应商库存系统

4.4 如果题目还要求客户旅程图

样卷有一个特殊要求:「用同一张客户旅程图,标出每个阶段的期望由哪些用例回应」。这不是画在图上的东西,而是一张两列表:

旅程阶段该阶段的期望回应的用例
报修前想快点找到人Report Fault
等待上门想知道工程师几点到Track Engineer Arrival
上门中希望一次修好Update Work Order、Request Parts
上门后想评价、想查历史Rate Service

这张表比图更容易拿分,因为它是唯一能证明「你的用例不是凭空想的」的东西。看到题目里出现 journey map,就一定要配这张表。

4.5 三个扣分点

  • 漏角色。 最容易漏的是「外部系统」(供应商接口)和「被通知的人」(客户在 Track Engineer Arrival 里是被系统推送的);
  • 用例名写成名词。Appointment 不是用例,Book Appointment 才是;
  • 满图箭头。 只有系统主动推送时才画箭头向 actor,其余画实线。

4.6 如果题目接着要求「精炼」

样卷的 1d 是独立一问:「Refine … to show include, extend and generalization of use cases and actors」。注意最后两个词 —— 很多人只做了用例侧,参与者泛化漏掉就丢 2 分。

用例图画完之后,精炼只需要在三处找东西:

找什么判断画法
公共步骤这一步每次都必须做吗?抽成被包含用例,«include» 箭头由基用例指向它
条件行为只在某些情况下才发生吗?做成扩展用例,«extend» 箭头由它指向基用例,条件写箭头旁
共同角色两个角色共用一整套用例、只是权限不同吗?泛化到父角色,空心三角由子指向父

精炼用例图的前后对照:左侧是精炼前,三个用例各画各的,并列出三个可以精炼的地方——校验工单状态在三个用例里都有(用 include)、撤回申请只在提交后五分钟内(用 extend)、客服与片区经理共用一整套用例(用泛化)。右侧是精炼后,三条 include 从基用例指向被包含的校验工单状态,一条 extend 从撤回申请指向申请配件,客服与片区经理泛化到一个抽象的门店员工父角色

三条判据里最实用的是第一列的提问法:「这一步能不做吗?」 不能不做 → include;可能不做 → extend。这一句能解决九成的判断。

5 第 1c 题:活动图(8 分)

题目要求:任选两个业务关键用例画活动图,必须包含实体,排除登录与生成报表。

5.1 选哪两个

挑标准:步骤多、有判断、跨角色。本例最典型的是:

  • Assign Engineer(派单) —— 有「工程师是否可用」的判断,跨客服与工程师两条泳道;
  • Request Parts(申请配件) —— 有「库存是否充足」的判断,跨工程师、系统、仓管三条泳道,且要读写多个实体。

5.2 成品:申请配件的活动图

申请配件的活动图:三条竖直泳道分别是工程师、系统、仓管。流程从实心圆开始,经提交备料申请、校验工单状态、查询供应商库存,进入「库存是否充足」的判断菱形;有库存时进入仓管泳道生成出库单,无库存时由系统把工单置为待件挂起,两条分支汇到结束状态。图右侧竖排四个业务实体(工单、库存项、出库单、配件消耗),并用两条橙色箭头示范读与写的相反方向——读是实体指向动作,写是动作指向实体;其余实体的读写已用括号标在动作框里

这张图有三处是得分点,值得单独说:

  1. 泳道 —— 一眼看出「这一步谁做」。本例里流程在三条泳道之间来回,正好暴露了协调成本;
  2. 判断菱形带方括号条件 —— [有库存] / [无库存];
  3. 实体的读写方向 —— 查询库存是读(实体 → 动作),回填工单是写(动作 → 实体)。这一笔写反是这类题最常见的扣分点。

5.3 配套的活动流程表

图上只能表达结构,精确的东西要靠表。这一张表是加分项:

活动执行者条件说明
提交备料申请工程师工单状态 = 进行中填配件编号与数量
查询供应商库存系统—调供应商库存接口
生成出库单仓管库存 ≥ 申请量扣减库存
工单置为待件挂起系统库存 < 申请量同时通知客户改约

5.4 两个扣分点

  • 画了确认框和错误提示。 讲义明确说这一阶段忽略异常,只保留业务决策;
  • 实体箭头全画成一个方向。 读和写是相反的,见上表。

6 第 1d 题:界面草图 / 仪表板(4 分)

题目要求:画出让客服、工程师、片区经理都能排期与管理工单的仪表板,并演示如何用隐喻更好地呈现信息。

只有 4 分,但判分看三件事:

  1. 信息选对了吗 —— 题目里提到三个角色,那屏幕上必须能分别回答他们的关切;
  2. 隐喻在哪 —— 并明确标注出来;
  3. 是否可读 —— 区块有标题,有主次。

6.1 成品图

派单仪表板草图:屏幕分三块。左上是周历视图,横向周一至周五、纵向上午下午,格子里填工程师姓名与工单号,空格标「空闲」,满档标「请改约」,右上角标注隐喻为日历等于排期。右上是待办请求列表,每条前有红黄绿灯标示紧急度,右上角标注隐喻为红绿灯等于紧急度。下方是四列看板:待派单、进行中、待件挂起、已完成,右上角标注隐喻为看板列等于状态流转。底部一行是本周完成率、平均上门时长、缺料单占比、满意度四个数字

6.2 三行说明(必须写)

这三行是拿分的关键,写在图下面:

  1. 周历隐喻排期 —— 客服一眼看出哪个工程师哪个时段空着,不用再一个个打电话问;
  2. 红绿灯隐喻紧急度 —— 片区经理扫一眼就知道哪几单快超时了;
  3. 看板列隐喻状态 —— 「待件挂起」这一列直接对应用例里的例外流程,让仓管知道该去催货了。

三行说明的每一行都要回到题目。如果题目说「用隐喻更好地呈现信息」,那说明里必须出现「隐喻」两个字,否则阅卷人可能认为你没做到。

6.3 一个提醒

4 分的题不要画 15 分钟。十分钟以内画完 + 三行说明,就足够拿分。不要在涂色和图标上花时间。

7 第 1e 题:测试用例 + 测试数据(4 分)

题目要求:给一张界面截图,要求「设计 2 个测试用例和 1 组测试数据」。

这道题的答案不是文字,而是一张固定的表。表头就五个字段,少一个都算不完整:

Test ID测试场景前置条件测试数据预期结果

7.1 本例的成品答案

被测界面是「申请配件」表单,其中数量字段限定 1–20。这个范围就是出题的抓手 —— 等价类天然分成三段:

测试用例与测试数据表的填法示例:上方是被测界面的字段清单,其中数量字段限定 1 到 20;中间是三条测试用例,TC-01 是范围内取中间值的有效用例,TC-02 是数量为 0 的下边界外用例,TC-03 是数量为 21 的上边界外用例,三条的数据和预期结果都写成了具体值;下方左块解释为什么这样挑数据(一个有效类加两个无效类),右块列出两个最常见的错误——两条用例都是正常路径、测试数据写成 invalid data

7.2 两条铁律

  • 一条有效、两条无效,不要全是正常路径。无效那条优先挑边界值(下边界外 0、上边界外 21),命中率最高(笔记 14);
  • 「测试数据」必须是真能填进输入框的值,不能写「invalid data」。这一列空着或写虚的,这题基本拿不到分。

7.3 一个易误解的措辞

题目说「1 组测试数据」,指的不是「只写一个数字」,而是「支撑这些用例所需的整组输入」—— 配件编号 + 数量,一起给全。把它理解成单个值,就会漏掉必填字段。

8 第 2 题:顺序图(10–12 分)

题目要求:给出某用例的 <Basic flow of events>,要求画 analysis model 的顺序图。

8.1 本例的 basic flow

Use Case: Request Parts(申请配件)

  1. 工程师在申请界面选择工单,输入配件编号与数量,提交申请。
  2. 系统校验工单状态是否为「进行中」。
  3. 系统根据配件编号查询当前库存。
  4. 系统调用供应商库存接口,获取该配件的可售数量。
  5. 系统比对本地库存与供应商可售量,判断能否满足申请。
  6. 若可满足,系统生成出库单,并把库存数量扣减。
  7. 系统把申请记录写入工单。
  8. 系统通知仓管有新出库单待处理。
  9. 系统向工程师显示「申请成功,请到仓库领取」。
  10. Use case terminates.

8.2 逐行翻译成消息

这是全卷最机械、也最容易拿满分的题,因为步骤和分数是一一对应的。逐行对照:

flow 步骤谁来发起发给谁说明
1:Engineer:PartsForm提交申请
2:PartsForm:PartsController转给控制器
3:PartsController:StockItemgetStock(),虚线返回
4:PartsController:SupplierAPI外部系统是边界对象,不是实体
5:PartsController自己比对,自身方法
6:PartsController:IssueNote创建出库单(写)
7:PartsController:WorkOrderaddPartUsage()(写)
8:PartsController:Storekeeper通知(这里是系统主动推送)
9:PartsController:PartsForm返回结果显示

8.3 成品图

申请配件的顺序图:五条生命线从左到右依次是工程师(actor)、申请界面(boundary)、配件控制器(control)、库存项(entity)、供应商接口(boundary 外部)。消息按 1 到 9 编号:提交申请、转控制器、查本地库存并以虚线返回、调供应商接口并以虚线返回、控制器自身比对库存、创建出库单、写入工单的配件消耗、通知仓管、最后以虚线返回成功消息给界面。控制器的激活条最长,库存项与供应商接口的短而窄,一眼能看出谁在等谁

8.4 四个细节

  1. 生命线顺序::Actor | :Boundary | :Control | :Entity…| :ExternalSystem。按这个顺序摆,交叉线最少;
  2. 外部系统是边界对象。 第 4 步的供应商接口画成 boundary,不是 entity;
  3. 「读」和「显示」是两件事。 第 3–4 步是控制器去取数据,第 9 步才是界面显示。把显示画成控制器自己完成,是职责错位的典型;
  4. 消息编号。 讲义说编号是可选的,但画了能让「我覆盖了全部 10 步」一眼可见。

9 第 2b 题:类图(6–10 分)

题目要求:从顺序图导出类图,不要求定义属性和方法。

看到「not required to define the attributes or methods」这句就别写属性和方法,只画类名(带构造型)+ 关系,省下的时间检查箭头。

9.1 导出规则

导出是机械的(笔记 22):

顺序图里的东西对应到类图
每个参与对象一个类
对象收到的消息该类的一个操作
谁调用谁类之间的关系

9.2 成品图

类图成品:全部用虚线加开放箭头的依赖线,因为这是分析模型。最左是工程师参与者指向申请界面边界类,界面指向配件控制器控制类;控制器往右指向供应商接口边界类,往下分出三条依赖分别到库存项、工单、出库单三个实体类。底部两块说明:左边列出三条关系限制(boundary 不直连 entity、actor 只接触 boundary、不同构造型之间不做继承聚合组合),右边是机械的导出规则

9.3 三条关系限制(直接的踩分点)

关系能不能用说明
boundary 直接关联 entity不推荐中间应隔着 control
actor 与 entity / control 直接关联不推荐actor 只与 boundary 交互
不同构造型之间做继承 / 聚合 / 组合无意义只在同类构造型内部用

如果题目只给一段用例描述、不给顺序图(C25 样卷的 2b),做法反过来:先在草稿上推几条消息,再从中抽类。

10 第 3 题:坏味道 + 重构(6–12 分)

10.1 真题的套路

C25 真题和 C25/C26 样卷是同一个套路:给两个类,其中子类继承了一堆自己用不上的东西。

  • C25 真题:BlacklistedTrainingProvider extends TrainingProvider —— 黑名单机构继承了一个「能开课」的机构,还多出 blacklistedReason、blacklistedDate;
  • C25/C26 样卷:SubContractedProject extends Project —— 分包项目的成员由分包公司提供,于是父类的 addMember() 被改写成空方法,removeMember() 直接 return null。

这就是被拒绝的馈赠(refused bequest)。它同时暗示了另一个问题 —— 继承的语义用错了。

10.2 如果换成这个案例

考试不一定考同一个套路。用本篇的案例试一次,你能看到它在别的形态下也成立:

java
public class Engineer {
    protected String id;
    protected String name;
    protected List<Skill> skills;
    public void accept(WorkOrder order) { ... }
    public void requestParts(String partNo, int qty) { ... }
}

// 外包工程师:不接单、不领料,只做远程技术支持
public class OutsourcedEngineer extends Engineer {
    @Override
    public void accept(WorkOrder order) {
        // Do nothing
    }
    @Override
    public void requestParts(String partNo, int qty) {
        return;   // 外包方自行备料
    }
    private String vendorName;
    private String vendorContact;
}

看出问题了吗?两个方法被重写成空 —— 这正是 refused bequest 的典型症状。而且语义上也不对:外包工程师不是一种「接单工程师」,只是碰巧也有 id 和 name。

10.3 答题分两段写

(a) 指出坏味道 + 用什么手法(通常 6 分)。 写成三句话:

  1. 坏味道的名字:Refused Bequest;
  2. 证据:accept() 被重写为空、requestParts() 直接 return(「子类拒绝接受父类给的馈赠」);
  3. 手法:Replace Inheritance with Delegation(用组合代替继承)+ Extract Superclass(抽出只含公共部分的父类)。

(b) 改设计,画类图(不要求写代码)。

重构前后的类图对照:左边重构前,外包工程师继承接单工程师,accept 与 requestParts 两个方法都被重写成空,红字标出坏味道为被拒绝的馈赠。右边重构后,抽出只含 id 与 name 的抽象人员类作为父类,接单工程师持有技能列表并能接单领料,外包工程师改为持有一个 Vendor(组合关系,空心菱形贴在外包工程师这一端),不再继承接单工程师。底部一行给出收尾那句话:新增一种工程师类型只需新增一个类

三种改法各自的适用条件:

情形改法图上的样子
两者共享大部分状态与行为,只有一小块不同Extract Superclass,把共同部分上移Person(公共)← Engineer / OutsourcedEngineer
差异是「能力上的有无」(能不能接单)改成组合 + 接口OutsourcedEngineer 持有一个 Vendor,不再继承 Engineer
只是行为契约相同、实现完全不同引入接口(判断见笔记 24)<<interface>> Assignable

10.4 收尾那一句

画完图,再用一句话说明「新结构下新增一种工程师类型要改几个类」:

改后新增「实习工程师」只需新增一个类,不再需要重写任何空方法;改前则必须先继承 Engineer,再覆盖两个自己用不上的方法。

这句话是给「为什么这个改法更好」收尾的,几乎每次都能拿到最后一两分。

11 交卷前的自检

11.1 五分钟清单

  • 每张图都有标题(Figure 1: Use Case Diagram for Dispatch Platform),标明对应哪一小问;
  • 用例图有系统边界框,actor 全在框外;
  • «include» 指向被包含的用例,«extend» 指向基用例 —— 方向没画反;
  • 活动图里有实体,读写箭头方向正确;
  • 顺序图里 actor / boundary / control / entity 四种生命线都出现了;
  • 类图里的关系都带了正确的符号(虚线箭头 / 实线 / 空心菱形 / 实心菱形 / 空心三角);
  • 所有「不确定」的地方都写了 Assumption。

11.2 开卷考试的操作细节

讲义里那句「Please make sure that you know how to draw all the UML diagrams using online tool」是硬要求:答案要交电子稿,图必须现场用工具画。所以考前一定要把这件事演练成肌肉记忆:

  • 选定一个工具并练熟(draw.io / WhiteStarUML / StarUML / Visual Paradigm 都行),知道它在哪里画 «include»、空心菱形、空心三角;
  • 知道怎么导出成图片并贴进答题文档,不要等到最后一刻才发现导出糊了;
  • 准备好一张符号速查卡(一页纸,开卷允许带)。内容就是笔记 28 那张图的打印版。

本节自测

  1. 150 分钟的答题时间,投放顺序应该遵守哪两条原则?为什么「先做文字题」是坏策略?
  2. 拿到长案例,按哪三栏去扫?为什么不该逐句精读?
  3. 数据驱动分析表里,Predictive 和 Prescriptive 的区别在哪?各举本例中的一个例子。
  4. 用例图被要求「complete」时,你怎么保证不漏角色?本例里最容易被漏掉的那个角色是谁?
  5. 活动图的题目里「include entities」这一句,具体要你多画什么东西?哪一支箭头最容易画反?
  6. 界面题只有 4 分,判分实际看哪三件事?三行说明为什么必须提到「隐喻」?
  7. 顺序图的分数是怎么和 <Basic flow of events> 的编号对应的?第 4 步「调用供应商接口」应该画成什么对象?
  8. OutsourcedEngineer extends Engineer 为什么属于 refused bequest?三种改法各自的适用条件是什么?

参考答案

  1. 画图题优先、能查书的题垫后。因为文字题写多了不会多给分(阅卷按要点给分,写长不加分),而图没画完一定扣分(§1.3)。
  2. 按 「谁、什么事、想要什么」 三栏扫。因为案例里几乎每句话都能落到某道题上,逐句精读会把时间花在背景交代上;按三栏扫完,答题材料就成型了(§2)。
  3. Predictive 说「下周哪些配件会缺」,Prescriptive 说「现在该做什么动作」。区别在有没有落到动作 —— 本例的 Prescriptive 是「自动比对库存与供应商可售量,低于安全库存就生成补货建议并推送仓管」(§3.1)。
  4. 按「有自己目标的人或外部系统」逐个抄。本例最容易漏的是配件供应商库存系统 —— 它是外部系统,但确实是 actor;其次是「被通知的客户」,他在 Track Engineer Arrival 里有自己的目标(§4.1)。
  5. 要给动作挂上业务实体,并画出读/写两种方向的箭头。最容易画反的是读:读是「实体 → 动作」,写是「动作 → 实体」(§5.2)。
  6. 看信息是否选对、隐喻是否明确标出、是否可读(有主次)。三行说明必须提到「隐喻」,因为题目要求的就是「demonstrate how metaphors can be used」,不点明就等于没答(§6.2)。
  7. 每个编号对应 1–2 条消息,分数大致按「消息覆盖度 + 职责正确性 + 符号规范」给。第 4 步的供应商接口要画成边界对象(boundary),不是实体 —— 判断依据是「你需要写代码去和它对接」(§8.2 / §8.4)。
  8. 因为它继承了父类的两个方法却都重写成空(accept() 空实现、requestParts() 直接 return),这就是「子类拒绝接受父类给的馈赠」。三种改法:共享大部分 → Extract Superclass;差异是能力有无 → 组合 + 接口;只有契约相同 → 引入接口(§10.2 / §10.3)。

术语表

术语中文一句话解释
basic flow of events基本事件流用例描述里「一切顺利」时的编号步骤序列
activity flow table活动流程表逐行记录活动名与说明的表格,与活动图配套
customer journey map客户旅程图用户完成目标的阶段、触点与情绪记录
metaphor隐喻借真实世界的形态组织界面信息,如日历、看板
assumption假设题目信息不足时自己补的前提,必须写在答案里
descriptive analytics描述性分析看清过去发生了什么
diagnostic analytics诊断性分析找出过去为什么发生
predictive analytics预测性分析推测接下来可能发生什么
prescriptive analytics处方性分析给出应当采取的动作或提醒
use case realization用例实现用对象协作把用例流程具体化,产物是顺序图
boundary / control / entity边界 / 控制 / 实体对象分析模型里三种职责不同的对象
refused bequest被拒绝的馈赠子类继承了用不上的父类成员,只能空着或返回 null
replace inheritance with delegation用委派代替继承把「继承」改成「持有 + 转发」的重构手法
extract superclass提取父类把多个类共有的部分上移成新的父类
pull down method下移方法把父类里用不上的方法推到真正需要它的子类
test case测试用例测试场景、前置条件、测试数据与预期结果的组合
boundary value analysis边界值分析在等价类边界上下取值测试
traceability可追溯性答案的每一步都能对回题目的某一句话

© 2026 HuanYu Blog

记录技术,也记录思考