上午题交卷时心里还有底,下午案例题一出考场,很多人就开始怀疑自己——知识点明明都背过,图也画了,字也写满了,可就是不知道能拿几分。2024上半年的软件设计师案例题,整体沿用了下午卷的经典版式:五道大题,覆盖结构化分析的数据流图、数据库设计与E-R转换、面向对象建模的UML图、算法设计与C语言代码填空、面向对象程序设计填空。它的难点从来不在"知不知道这个概念",而在于能不能在有限时间里,从一段两三千字的业务描述里抠出正确的实体、正确的数据流、正确的多重度,再用手写的方式规范地画出来。这篇内容面向正在准备这类考试的读者,也面向那些已经考过一次、想知道自己到底错在哪的人。我会把每一类题型的判分逻辑、通用解法、以及我自己和身边人反复踩过的坑摊开讲清楚,让你看到案例题其实是一门"信息提取+规则套用"的手艺活,而不是玄学。
1. 下午卷五道题的版式逻辑与失分真相
1.1 五道题的固定分工决定了复习的投入比例
先把这张表放在最前面,因为它直接决定了你时间该怎么分配。下午卷的题量、分值、题型在近些年高度稳定,这不是秘密,但很多人复习时的投入比例和分值比例完全不匹配——花两周啃设计模式,却只花两天练数据流图,这属于典型的用错力气。
| 题号 | 题型 | 常见分值 | 核心考察能力 | 可预期程度 |
|---|---|---|---|---|
| 第1题 | 数据流图(DFD) | 15分 | 结构化分析与信息提取 | 极高,几乎必考 |
| 第2题 | 数据库设计(E-R、关系模式、规范化) | 15分 | 建模与范式判断 | 极高 |
| 第3题 | UML图(用例、类、状态、顺序) | 15分 | 面向对象建模 | 极高 |
| 第4题 | 算法设计与C代码填空 | 15分 | 算法理解与逻辑推理 | 高,算法种类浮动 |
| 第5题 | 面向对象程序设计填空(Java/C++) | 15分 | 代码阅读与框架理解 | 高 |
看懂这张表你就会明白:前四题中,前三题几乎全是"规则型"题目,答案对不对取决于你有没有掌握画图和标注的规则,而不是取决于你聪不聪明。这类题是整张卷子里性价比最高的部分,理论上45分的题,练熟了应该稳定拿到38分以上。第4、5题才是真正吃基本功的地方,但因为填空位通常有4到6个,每个空独立给分,所以即使代码读得磕磕绊绊,也能拿到一半以上的分数。
我在复习时做过的统计是:把前3题的"规则清单"完整梳理一遍,大约需要三天;把第4题常考的七八种算法骨架吃透,需要两周;第5题的代码阅读能力,靠的是长期积累,短期突击收益有限。所以如果你的复习时间只剩一个月,正确的顺序是先锁死前3题的规则分,再去啃算法,最后才是代码填空。反过来做的人,往往会陷入"算法题研究了很久还是不稳定,回头一看数据流图基本没练"的尴尬。
1.2 看得懂知识点却拿不到分,问题出在"格式分"
这是案例题最反直觉的一点。绝大多数失分不是因为"不懂",而是因为"不规范"。案例题是人工阅卷,阅卷人手里拿着一份标准答案,答案是结构化的:这个空必须填"外部实体:维修人员",你写"维修部门的人来操作",意思再对也很难给分。数据流图必须从数据存储指向加工、从加工指向外部实体,你箭头画反了,逻辑再自洽也是零分。
我见过太多人复盘时的心态是"我明明想到了啊"。想到了不算分,写对才算分。所以案例题的训练方式应该和上午题彻底分开:上午题是"识别正确答案",案例题是"生产标准答案"。前者靠刷选择题,后者必须动笔,必须写在纸上,必须对着标准答案逐字比对。只做前者的人,考场上会出现一个非常典型的症状——读完题觉得都会,动笔时发现名字叫什么、箭头朝哪、多重度标在哪,全都不确定。
提示:案例题一定要手写练习,至少完整手写三套。用电脑打字练习的效果会打折扣,因为你不会意识到自己写字速度有多慢、图示布局有多乱。
1.3 我的答题顺序与时间切分
下午卷总时长两个半小时,五道题,平均每题三十分钟。但这个"平均"是陷阱。前三题虽然规则性强,但阅读量大——一道数据流图题的背景描述往往有一千五百字以上,逐字读一遍就要五六分钟。如果你按三十分钟平均分配,很容易在第4题算法上卡住二十分钟,然后前面题目的检查时间被彻底挤掉。
我习惯的顺序是:第1题、第2题、第3题先做,第5题次之,第4题放最后但保证留足二十五分钟。理由是前三题的答案是"渐进产出"的——即使某一个小问不确定,其他小问也能拿到分,写一点得一点,先拿到手最安心。第5题代码填空虽然难度不低,但填出来的空是有把握的,不会出现"写了一堆全错"的情况。第4题算法反而最容易出现"思路卡壳、时间黑洞",放最后能有效止损。
时间切分的具体做法是:每做完一题,立刻在草稿纸角落记下当前时间。如果第1题做完已经过去四十五分钟,说明你的阅读速度或者画图速度有问题,后面必须加快。这种实时的自我监控比事后懊悔有用得多。我自己的节奏是前三题各三十分钟,第5题二十五分钟,第4题二十五分钟,剩下二十分钟用于返工——尤其是数据流图的箭头方向和多重度标注,这两处是最容易在紧张中写错的,必须留时间回头看一眼。
1.4 题面里的"隐藏条件"是怎么布置的
案例题的背景描述不是平铺直叙的说明文,它埋了大量的判定条件,而且往往以很不显眼的方式出现。比如描述里说"每个设备可以有多个维修记录,但每条维修记录只对应一个设备",这句话就是多重度判定的直接依据;说"系统允许同一个人在不同部门担任不同职务",这就是在提示你不能简单地把人和部门合并成一个实体。
这些句子有个共同特点:它们通常出现在段落中间,用"但""同时""若""只有"这类词引出来,而且往往不是后面问题的直接提问对象。多数人读题时会不自觉地跳过这类限定语,只记住主干。我的建议是,读第一遍时用笔把所有的量词、限定词、条件句划出来——"多个""唯一""每个""只能""必须""至少一个"。这些词在后面的问题里几乎每个都会对应一个得分点。这不是玄学,而是结构化分析的题面本身就是按数据模型来写的,出题人写"多个"的时候,脑子里想的就是一对多关系的标注。
2. 数据流图:分层平衡与缺失数据流的定位方法
2.1 父子图平衡是最硬的扣分点
数据流图这一题通常分三到四个小问:补充外部实体、补充数据存储、找出缺失的数据流、有时还要求写出数据字典条目。第一小问往往是最简单的送分题,第四小问最难。但无论哪一问,有一条规则会贯穿始终,那就是父子图平衡——顶层图(0层图)里某个加工的输入输出流,在它展开的下一层图(1层图)里必须原封不动地出现。
这条规则之所以关键,是因为它提供了一个纯粹的机械检查手段。当你被问到"图X中缺少了哪条数据流"时,如果题目给了两张图,你几乎可以靠对比完成:找出子图的边界,看它的输入流和输出流,然后回到父图看那个对应的加工框,进出的流是不是一一对应。少的那条,就是答案。
我拿一个自己按真题结构编的场景来说明。假设题目描述的是一个"设备报修管理系统":报修人员提交报修单,系统校验后生成工单派给维修人员,维修人员完成后录入维修记录,管理员可以查询统计。0层图有三个加工:受理报修、派单调度、维修归档。受理报修的输入是"报修单",输出是"已校验报修单";派单调度的输入是"已校验报修单",输出是"工单";维修归档的输入是"工单"和"维修记录",输出是"归档记录"。现在看1层图里"派单调度"被展开成三个子加工,如果子图里出现了一条叫"维修人员空闲状态"的输入流,而0层图里没有对应,这就是父子不平衡——你要么在0层图补上,要么指出这条流是多出来的。反过来,如果0层图显示"派单调度"需要输出"派工通知"给外部实体"维修人员",而子图里这条流消失了,那就是典型的"缺失数据流"。
画图时还要注意一点:数据流不能凭空产生。一条流必须有明确的起点和终点,起点和终点只能是外部实体、加工、数据存储三者之一。很多人答题时习惯在两条流之间画个箭头连接,这在规范上是不允许的。另外,外部实体之间不能直接有数据流,数据存储之间也不能直接有数据流——一切数据交换必须经过加工。这两条约束看起来是常识,但在紧张的考场上,我确实见过有人把"报修人员"和"维修人员"直接用箭头连起来,理由是"他们要沟通"。
2.2 找外部实体和数据存储的排除法
补充外部实体的题目通常给出四到五个空,答案是题目描述里那些"系统之外但和系统有交互的角色或系统"。判定的思路很简单:这个对象是不是在系统内部被处理?如果它只是提供数据或者接收结果,那它就是外部实体。值得注意的是,外部实体不一定都是"人",也可能是另一个信息系统,比如"财务系统""短信通知平台"。
但真正容易出错的地方在于名称的一致性。如果题面的描述里叫"报修人员",你的图里就不能写"报修人"或者"申请人"。听起来有点吹毛求疵,但阅卷是按点给分的,名称不一致会直接导致这个点被判错。我在练习时的做法是:读题时把所有出现的角色名称、单据名称、数据名称单独抄在草稿纸上,形成一个"词表",画图时直接从词表里取词,绝不自己造新词。这个小习惯能救回不少冤枉分。
补充数据存储的题目会稍难一点,因为数据存储往往没有在描述里被显式命名。判断依据是:系统需要"记住"哪些信息?凡是需要保存、后续要被查询或更新的信息,就是一个数据存储。比如"报修单""工单""维修记录""设备台账",这些都需要持久保存。反过来说,如果一个东西只是流经系统、处理完就结束,那它只是数据流,不是数据存储。数据存储的命名一般用"XX表"或"XX记录",并且要标注清楚它和哪些加工之间有读写关系——一条流从加工指向数据存储叫写入,从数据存储指向加工叫读取,方向错了同样是失分。
2.3 缺失数据流的排查顺序
这是整道题里最考验耐心的一问。我的固定排查顺序是这样的:
- 先看每个加工的输出流够不够。加工的产物如果要给外部实体,必须有输出流;如果加工的结果要保存,必须有指向数据存储的流。
- 再看每个数据存储的流向是否闭环。一个数据存储如果只有写入、没有任何读取,通常意味着某条读取流缺失了——因为存起来不用是没有意义的。
- 接着检查外部实体是否有"只出不进"或者"只进不出"的情况。如果某个外部实体提供了数据却从来没收到任何反馈,那大概率缺一条输出流。
- 最后回到父子图平衡,逐条比对边界流。
这个顺序的价值在于它是收敛的。你不必靠灵感去"猜"缺哪条流,而是像查电路一样一个节点一个节点地过。练过五六道之后,你会发现缺失的数据流往往集中在两三个位置上:数据存储的读取方向、外部实体的反馈方向、以及跨加工的中间结果传递。这三种位置覆盖了绝大多数考题。
注意:写缺失数据流时,必须写清"从哪个对象到哪个对象",只写流名称是不够的。标准答案的格式通常是"XX → YY,数据流名称为ZZ",三条信息缺一不可。
2.4 数据字典题的低成本拿分法
数据字典一般是要求你写某个数据流或者数据存储的组成。它的标准写法是自顶向下逐层分解,用"="表示定义,用"+"表示顺序组合,用"{}"表示重复,用"[]"配合"|"表示选择。举个例子,"报修单"可以写成:报修单 = 单号 + 报修人 + 联系方式 + 设备编号 + {故障描述} + 提交时间。如果某个字段本身还需要分解,就继续往下写一层。
这类题在整道大题里分值通常只有3到4分,但性价比很高,因为它几乎不依赖前面几问的正确性,独立作答即可。我在考场上会把它当成"必拿分"来处理,即使时间紧张,也要把这个小问写完。写法上的注意点是:括号必须成对,层级符号不能混用,字段名要和图里出现的名称对得上。这三条做到了,基本不会扣分。
3. 数据库设计题:E-R图判定与关系模式转换的取舍
3.1 什么时候必须把联系升级为实体
数据库题的典型结构是:给一段业务描述,要求补充E-R图中的实体或联系,然后把E-R图转换成关系模式,标出主键和外键,最后问某个关系模式属于第几范式。这里最容易卡住的是第一问——某个概念到底该画成实体,还是画成两个实体之间的一个联系?
判定的核心在于"这个联系本身是否携带属性"。如果两个实体之间的关系仅仅是"关联",比如"教师讲授课程",那它就是一个联系,不需要独立成实体。但如果这个关系有自己独有的属性,比如"教师讲授课程,需要记录授课学年和课时数",那么这个"授课"就应该是一个联系实体(也可以叫关联实体),它本身带属性,转换时会形成一张独立的关系表。
还有一个更隐蔽的判定条件:联系的类型。多对多联系在转换成关系模式时,必然要独立成表,因为它无法通过在某一张表里加外键来表达。一对多联系可以通过在"多"的那一端加外键来表达,不必独立成表。一对一联系则要看哪一端的参与是全部的,把外键放在参与度高的那一端。这个"联系类型决定表结构"的链条,是数据库题的核心逻辑,理解了它,后面几问就是机械操作。
我编过一道自测题来练这个逻辑:一个仓储场景,描述是"每个仓库可以存放多种物料,每种物料可以在多个仓库存放,且需要记录每个仓库中该物料的库存数量和库位编码"。这里的"存放"是多对多,且带属性(库存数量、库位编码),所以它必须独立成一张表,主键由仓库编号和物料编号联合构成。如果你把它简化成"仓库"表里的一个字段,整个模型就崩了。这类判断在考场上应该做到条件反射。
3.2 主键与外键的书写规范
转换之后的关系模式,标准写法是把所有属性用括号括起来,然后给主键加下划线,外键在题目要求时用单独一行注明。很多人失分不是因为想错了,而是因为格式没对:主键没加下划线,或者用波浪线代替,外键写在该写的属性后面却漏了"参照哪个关系"。
我的处理方式是:先写属性列表,再统一处理标注,最后单独列一行"外键:XX 参照 YY(ZZ)"。这样即使某个属性判断错了,格式分至少不会丢。另外要注意的是,联合主键必须整个下划线连起来画,不能只画一半,因为联合主键是一个整体,缺一个属性它就不是同一个键了。
3.3 范式判断题的答法:说结论也要说理由
问到"这个关系模式属于第几范式,为什么",标准答案是两步:先给结论,再给理由。理由的表述是有套路的——如果存在非主属性对主键的部分函数依赖,就是2NF;如果存在非主属性对主键的传递函数依赖,就是3NF;如果存在主属性对主键的部分依赖或传递依赖,那才牵扯BCNF。很多人只写"因为有传递依赖",却不指出具体是哪两个属性和哪个主键之间形成了传递,这在阅卷时容易被判为表述不清。
我的做法是把依赖关系写成一个明确的链条:A → B,B → C,所以A → C 是传递依赖,其中C是非主属性,因此不满足3NF。这种写法虽然啰嗦,但清晰度极高,几乎不会因为表述含糊而失分。顺带说一句,如果题目要求"规范化到3NF",你就必须写出分解后的关系模式,而不是只说"应该消除传递依赖"——分解动作是要给分的。
4. UML与面向对象建模:三类图的答题套路差别很大
4.1 用例图:连线方向与包含扩展关系的区分
用例图这一题常见的要求是补充参与者、补充用例、标出包含(include)或扩展(extend)关系。参与者的判定标准是"谁或什么系统与系统发生交互",这一点和外部实体非常接近,因此数据流图练熟了,这里能直接迁移。真正的坑在连线上:参与到用例的关联线是没有箭头的实线(或者用箭头指向用例表示主动发起),而include和extend是带箭头的虚线,方向不能反。
include的方向是"基础用例指向被包含用例",语义是"我执行时必须做那件事";extend的方向是"扩展用例指向基础用例",语义是"某个条件下我会给基础用例加一段额外行为"。这个方向很容易记反,因为直觉上会觉得"是扩展加进来,所以应该指向基础"。我用的记忆方法是"包含是我叫你去,扩展是你来找我"——我做主体,我指向被包含者;扩展是被动附加,扩展方指向主体。
还有一个高频失分点:用例命名。用例必须是一个"动宾结构",表示一个对参与者有价值的目标,比如"提交报修单""查询库存",而不是"报修单管理"这种名词短语,更不是"校验数据"这种内部步骤。内部步骤是不能作为用例的,它属于系统内部的处理逻辑。这条规则在判断题里经常出现,见到名词化的用例名就要警觉。
4.2 类图:多重度标注与聚合、组合的判断
类图题通常要求补充类名、补全多重度、判断两个类之间的关系类型。类名的补充线索全部藏在题面描述的"名词"里,把描述里所有的名词圈出来,剔除掉它只是属性或者只是值的那部分,剩下的就是类。这一步和数据流图找实体的思路是一致的。
多重度的判断则要盯住描述里的数量词。一对一的典型表述是"每个订单对应一张发票";一对多的表述是"一个部门有多名员工,每名员工只属于一个部门";多对多的表述是"一名学生可以选修多门课程,一门课程可以被多名学生选修"。标注时要注意位置:多重度标在关联线的两端,靠近哪个类就表示哪个类的对象数量,这个位置一旦标反,语义就整个颠倒了。
聚合和组合的差别,说白了就是"能不能同生共死"。组合关系里部分不能脱离整体存在,整体销毁部分也销毁,图上用实心菱形;聚合关系里部分可以独立存在,用空心菱形。判断依据是看题面有没有暗示"这部分是整体专属的"。比如"订单"和"订单明细",明细离开订单没有意义,是组合;"部门和员工",员工离职后部门还在,员工也可以调到别的部门,这是聚合。至于泛化,就是"is-a"的关系,子类继承父类,用空心三角形指向父类,方向是指向父类的,这一点也常有人画反。
4.3 状态图与顺序图:事件驱动与消息时序的差别
状态图考的是"对象的生命周期",核心要素是状态、事件、迁移、守护条件。补状态的时候,答案往往在描述的动词短语里——"订单提交后进入待审核状态,审核通过后变为已确认"。这里的"待审核""已确认"就是状态名。补事件的时候,答案通常是触发状态变化的外部动作,比如"审核通过""取消订单"。
顺序图考的是"一次交互中对象的协作顺序",纵向是时间轴,横向是对象。补消息的时候要特别注意同步消息和异步消息的箭头差别:实心箭头是同步调用,需要等待返回;开放箭头是异步消息,发出就不管了。返回消息用虚线箭头。这些细节分值不高,但都是明确的知识点,会就是会,不会就是不会,没有中间地带。
实际答题时,我建议先画能确定的,把不确定的留到最后,因为图题有很强的"互锁"效应——一旦某个类名或状态名判断对了,与之相关的几条线也跟着确定下来。反过来,如果一开始就在最难的那个空上死磕,很容易把整道题的节奏打乱。
5. 算法设计题:把代码填空当成逻辑推理来解
5.1 先读问题描述的最后一段,再回头看代码
算法题的标准结构是:先给一段问题描述(比如"求最长递增子序列的长度"),再给一段C语言代码框架,中间挖四到六个空,最后问时间复杂度和空间复杂度。很多人的阅读顺序是从头读到尾,结果在描述里绕了十分钟还没进入代码。更有效的顺序是:先看问题描述的第一句(它通常直接点明算法要解决的问题),再看代码的函数签名和变量名,最后回头细读描述中的算法思路部分。
函数签名和变量名给出的信息量远超想象。比如函数名叫findLongest,参数里有int a[], int n,返回值是int,基本可以确定是在求某个最长长度;如果内部出现dp[]数组和两层嵌套循环,那十有八九是动态规划。先建立这个整体认知,再去填空,效率会高很多。
5.2 高频算法骨架与对应的填空套路
近些年的算法题集中在几类骨架:动态规划(最长公共子序列、背包、最大子段和)、贪心(区间调度、活动选择)、图遍历(DFS、BFS、连通分量)、回溯(排列组合、子集)、以及部分排序类问题。每一类骨架的挖空位置都有规律。
动态规划的挖空通常在三个位置:状态转移方程、边界初始化、结果返回。状态转移方程的空最典型,因为它往往是一个max或min表达式,你需要判断的是"取哪两个值比较"。判断方法是回到问题定义——如果当前状态是"以i结尾的最优解",那比较的通常是"接上i"和"不接i"两种选择。
贪心的挖空通常在比较条件上,比如按结束时间排序后,判断下一个活动能否接上的条件是a[i].start >= last_end。这类空的关键是搞清楚两个变量的物理含义,写之前先在草稿纸上标注一遍。
图遍历的挖空通常在递归调用或者队列操作上,比较隐蔽的是访问标记的设置时机——是在入队时标记还是出队时标记,这一点在BFS里会影响正确性,出题人也喜欢在这里设空。
我一般的做法是,拿到题先把代码整体通读一遍,搞清楚每个变量的含义,用铅笔在代码旁边写下"dp[i]表示什么""last_end表示什么"。这一步花五分钟,但能避免后面所有空都填得似是而非。
5.3 复杂度填空的判据
复杂度这一问基本是送分题,判断方法很直接:数循环的嵌套层数。单层循环O(n),两层嵌套O(n²),如果内层循环的边界随外层变化(比如快排的分区),那就是O(nlogn)。递归算法看递归树的高度和每层的工作量,二分查找是O(logn),归并排序是O(nlogn)。
但有一个常见的陷阱:题目里的代码可能带有剪枝或者提前退出,这时候最坏复杂度仍然要按完整情况写。还有一种情况是空间复杂度里忽略了递归栈的消耗,递归深度为n的话,空间复杂度至少是O(n)。这两点我在练习时都栽过,写答案时多问自己一句"最坏情况是什么",能避掉大部分错误。
5.4 填不出来时的部分分策略
算法题最忌讳的是空白。哪怕完全看不懂逻辑,也有几类空是能猜的:循环变量的初值通常是0或者1,取决于数组下标从哪开始;累加器的初值通常是0,乘积的初值是1;比较条件里的符号(>还是>=)可以通过代一个小例子验证;返回值的表达式通常和累加器或者dp数组的最后一个元素有关。
我通常在时间不够的时候,会把能填的空全部填上,不确定的也写一个最可能的答案,绝不留空。案例题是按空给分,留空等于主动放弃,写错至少还有概率蒙对,这两者的期望值完全不同。
6. 面向对象代码填空:靠读框架而不是背语法
6.1 类名和方法名本身就是提示
第5题给的是完整度较高的Java或C++代码,挖空的位置往往是方法体内部、继承关系中的抽象方法实现、或者接口的调用处。它的难点不在语法,因为语法部分通常不会挖空,而在理解这套代码的设计意图。
读懂设计意图的入口是类名和接口名。题目里如果出现AbstractHandler、ConcreteHandlerA、ConcreteHandlerB这样的命名,你不需要看代码内容就能判断这是责任链模式;出现Strategy和几个XxxStrategy,就是策略模式;出现Observer、Subject、notify,就是观察者模式。命名是出题人留给你的地图,顺着地图走就不会迷路。
6.2 设计模式在题目中的常见埋点
案例题里的设计模式不会考得很深,通常是"识别+填空"的组合。模板方法模式的埋点在于父类定义了一个流程方法,里面依次调用几个抽象方法,子类只需要实现抽象方法——挖空往往就在子类的具体实现里,你需要根据子类的业务语义写出来。策略模式的埋点在于上下文类持有一个策略接口的引用,挖空常在上下文类的构造函数或者执行方法里,你需要填的是"把具体策略传进去"或者"调用策略的某个方法"。
除了设计模式,还有一些纯面向对象概念的填空,比如多态调用、抽象类的实现、接口方法的实现。这类空的关键是看方法签名是否匹配——返回类型、参数列表必须完全一致,差一个类型就是编译错误。填空时把上下文的调用语句翻出来看一眼,参数是怎么传的,返回值是怎么用的,照着写就不会错。
6.3 时间不够时的止损方式
代码填空题有一个特点:几个空之间有时存在依赖关系,前面的空填错了,后面的空跟着错。所以策略上是先做独立性强的空,比如构造函数里的赋值、简单的getter/setter、明显的循环边界,这些空不依赖其他空的答案。那些需要理解整套流程才能填的空,放到最后。
如果实在读不懂某段代码,还有一个笨办法:看这个空所在方法的调用方,调用方传了什么参数进来,又拿返回值做了什么,反推这个方法该干什么。这个思路在调试真实代码时同样适用,本质上就是"从使用方推断实现方"。我在练习时用这个方法救回过不少空,尤其是那些涉及集合操作的填空。
7. 手写规范、错题归档与临场节奏
7.1 手写图的规范细节
机考环境下画图是用鼠标拖拽完成的,这反而是个好消息,因为线条会自动对齐,箭头方向不会画歪。但即便如此,仍然有几个规范必须遵守:外部实体用矩形,加工用圆角矩形或圆形,数据存储用两条平行线(开口矩形),数据流用带箭头的线并标注名称。图的布局要尽量从左到右,数据流向不要来回穿插,因为阅卷时看不清流向会直接影响判断。
还有一点容易被忽略:图的编号。分层数据流图的加工要有编号,0层图里的加工编号1、2、3,展开到1层图就变成1.1、1.2、1.3。这个编号体系是父子图对应的依据,漏编或者编号不对应,会让阅卷人无法确认你画的是哪一部分,从而影响整题的得分。我在练习时会专门检查一遍编号的连续性,这个动作只需要十几秒。
7.2 错题复盘要按失分类型归档,而不是按题号
大多数人复盘的做法是把错题重做一遍,做对了就算过了。这个方式对案例题效果有限,因为案例题的失分原因高度分散,同一道题可能既错在概念理解,也错在书写格式。更有效的做法是建立一个"失分类型表",把每次的错误归到下面这几类里:
| 失分类型 | 具体表现 | 改进动作 |
|---|---|---|
| 概念判定错误 | 该建实体的画成了联系,多重度标反 | 回到教材对应章节,重画三个同类例子 |
| 术语不统一 | 图里写"报修人",题面写"报修人员" | 养成先列词表再画图的习惯 |
| 方向性错误 | 箭头反向、include与extend搞混 | 整理一份"方向速查表"背下来 |
| 要素遗漏 | 忘标主键下划线、漏写外键参照 | 交卷前按清单逐项核对 |
| 时间不足 | 最后一题空白或只写了一半 | 严格计时训练,按题限时 |
这张表的用处在于,它可以告诉你自己的问题是"知识型"的还是"习惯型"的。知识型问题需要回去补对应章节,习惯型问题只需要建立检查清单。我自己的统计是,习惯型失分占了总失分的四成以上,这些分本来是完全不用丢的。
7.3 按套计时训练,别拿单题练
单题练习能解决知识点问题,但解决不了节奏问题。案例题的节奏感非常关键,因为五道题的阅读量加起来接近一万字,中间还要画十几张图。我建议至少完整地做三套模拟,全程计时,中间不查资料、不中断。做完之后不要立刻对答案,先自己给自己打分,估一估每题能拿多少,再对标准答案。这个"自评"的过程能训练你对得分点的敏感度——估得越准,说明你越清楚阅卷的判分逻辑。
计时训练还有一个附带好处:它会暴露你在哪类题上超时。有人是数据流图读题慢,有人是算法题卡壳,有人是代码填空反复犹豫。这些弱点在单题练习里根本看不出来,只有在完整的时间压力下才会显形。
7.4 几个考场上的小动作
最后分享几个我在考场上养成的习惯,都是些不起眼但确实能救分的小动作。第一个是开考后先花两分钟把五道题浏览一遍,确认题号和题型有没有变化,尤其注意有没有出现往年没见过的题型——如果第3题不是UML而是别的,你的时间规划就得当场调整。
第二个是每道题答完后,用半分钟做一次"三查":查名称是否和题面一致,查方向是否画反,查有没有漏标主键或外键。这三个动作加起来不到两分钟,但能挽回的分数往往在五分以上。
第三个是算法题的填空,一个空都不要留。哪怕只是把循环变量的初值写上去,也可能踩到一个得分点。案例题的判分是按点给的,空白是零概率得分,瞎写至少还有概率。
第四个是不要在某一问上死磕超过八分钟。案例题的每一问分值都不高,卡住一个空丢掉的时间,可能足够你做完后面两问。遇到实在想不出来的,写个最可能的答案先占位,把剩下的做完再回头。
这些经验谈不上高深,都是练出来的肌肉记忆。案例题这门东西,说到底是熟练度的问题——规则就那么些,题型就那么五类,练够三四十道,考场上的大部分不确定感都会消失。真正难的从来不是题目本身,而是愿不愿意动笔把每一道题完整地写一遍。