1. 先看清下午题六的“真面目”
1.1 分值、题位与给分逻辑
软件设计师的下午试卷,前四道题基本是固定的:数据流图、数据库设计、UML建模、算法与C代码填空。到了第五题和第六题,出题人给了你一个选择机会——第五题是传统的C语言程序设计,第六题则是以Java(有时是C++)为载体,考察某个经典设计模式在真实业务场景中的落地写法。
很多第一次备考的朋友看到“下午题六”这几个字就直接跳过,觉得设计模式是Java的专属,自己平时写C或者写脚本多,心里发怵。实际上,下午题六是整套下午卷里性价比最高的题目之一:分值固定在15分左右,题型高度稳定,代码量不大,空白处填的内容基本都是“类名、方法名、继承关键字、接口关系”这些有规律可循的东西。最重要的是,它的出题范围非常明确,翻来覆去就是那十几个经典模式,和上午题那些发散型概念题完全不是一个风格。
给分逻辑也很有特点。我去对过几次真题答案,发现它并不是按“一句完整代码”给分,而是按每个空独立计分。比如六个空各2分,模式名称2分,意图分析3分,凑齐15分。所以即使你某一段代码完全不会,其他空照样能拿分,不会出现“一步错步步错”的连锁扣分。这和算法题那种“主程序写不出来就基本没分”的判卷逻辑差别很大。
换句话说,下午题六是有套路可循的,甚至可以叫它“背模板题”。你需要的不是把整本设计模式书背下来,而是吃透高频模式的类图结构、代码骨架,以及出题人最爱的几个业务场景。
1.2 历年真题的出题规律
我把能翻到的历年下午题六大致过了一遍,发现出题规律非常明显。总体来看,十几个经典模式并不是均匀出场的,而是有一个明显的“热度梯队”。
最常出现的,是策略模式、观察者模式、适配器模式、模板方法模式、简单工厂模式/工厂方法模式、装饰器模式、单例模式。这几个加起来,占了历年下午题六的大半壁江山。次级梯队是组合模式、状态模式、迭代器模式、代理模式、抽象工厂模式。再冷门一点的比如建造者模式、责任链模式、备忘录模式,偶尔冒一次头,频率低,但也不是完全没考过。
为什么会是这种分布?很简单。软考的出题场景喜欢贴近信息系统开发,比如订单折扣、员工工资计算、图形绘制、日志记录、文件上传、消息通知这类日常业务。这些场景天然适合策略、观察者、模板方法、工厂这些模式来表达。反过来,像备忘录、解释器这种模式,在常规管理信息系统里很少作为核心流程出现,出题人也不好编场景。
这个规律对你的复习安排非常重要。如果你时间紧,先把高频梯队吃透,应付绝大多数考试年份完全够用。有时间再补次高频,冷门模式扫一眼类图和典型用途就行,不用死磕代码默写。
1.3 选Java还是选C?我的建议
下午第五题是C语言编程,第六题是Java/C++设计模式,两题任选其一作答。每年都有考生在考场前纠结半天,我见过不少因为这个选择吃亏的人。
我的看法很简单:如果你平时能读得懂Java代码,哪怕只是读过几段,也建议你选下午题六。原因有三。
第一,第五题的C语言题虽然看上去“亲民”,但考查的是链表、二叉树、图遍历这些数据结构操作,空位多且环环相扣,一个变量理解错,后续推理全崩。设计模式题则不同,代码结构是现成的,你填充的内容大多是补类名、补声明、补方法调用,局部性很强。
第二,设计模式题有“模式名称”和“设计意图”这种纯记忆加分项。就算代码填空填得不准,模式识别对了,理论小题答对了,依然能拿下一个可观的保底分。C语言题很难给这种白送分的机会。
第三,Java语法本身就是另一种形式的模板。interface、implements、extends、abstract,这些关键字长得就有固定的语言结构,看到接口定义你就知道要在哪儿补方法实现,看到抽象类你就知道要往哪儿挂子类。只要熟悉几个高频模式的代码骨架,考场上基本是“照猫画虎”。
当然,如果你是那种常年写C、看到class就头疼的老派选手,也别硬来。选择的标准只有一个:你在考场上见到哪种代码更不慌。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 高频设计模式逐类拆解
2.1 创建型:工厂、单例、建造者
创建型模式在下午题六里出场率不低,尤其简单工厂和工厂方法。这类模式的核心思想就一句话:把“创建对象”这件事从业务代码里抽出来,交给专门的类去处理。
简单工厂的代码结构最好认:一个工厂类,里面通常有一个静态方法,方法内部用if-else或switch判断参数,然后new出不同的子类对象返回。它的“模式味道”非常重,看到这种结构基本可以直接锁定。但注意,简单工厂不是GOF的23种模式之一,软考却特别爱考,所以别因为它不在正统名单里就轻视。
工厂方法比简单工厂更“正规”一点。它会定义一个抽象工厂类或接口,声明一个创建对象的抽象方法,然后让每个具体工厂子类去决定创建哪个具体产品。如果你在代码里看到abstract Product createProduct()这类方法签名,基本就是工厂方法没跑了。
单例模式考得更直白。核心套路是:私有构造方法、私有的静态自身实例、公有的静态获取方法getInstance()。出题人常让考生补private修饰符、补static关键字、补if (instance == null)判断,偶尔还会延伸到双重检查锁或枚举实现。单例的理论小题喜欢问“为什么要私有化构造方法”,答案本质就是防止外部通过new创建多个实例。
建造者模式在软考里出现频率不高,但也不能完全无视。它的典型特征是:一个产品类、一个Builder抽象类或接口、一个Director控制类。Director里调用Builder的多个构建方法,最终通过getResult()返回产品。代码里经常出现连续赋值的setPart()方法,识别起来不算难,但需要你提前对它有个基本印象。
2.2 结构型:适配器、装饰器、代理
结构型模式里,适配器和装饰器是下午题六的两大主力。
适配器模式的核心场景是“接口不兼容”。代码结构上,通常会有一个已有的Adaptee类,它有自己的方法名,但方法签名和目标接口Target对不上。适配器类Adapter实现Target接口,在接口方法内部调用Adaptee的方法。出题人最爱让你补的就是Adapter类里的那行“调用被适配者方法”的代码,比如adaptee.specificRequest()。
装饰器模式的识别信号是“层层包裹”。代码里会出现一个抽象组件Component,一个具体组件ConcreteComponent,还有一个装饰器抽象类Decorator继承Component并持有Component类型的成员变量。具体装饰器在调用完父类或组件的核心方法之后,再增加自己的额外行为。考场上如果看到构造方法里传入一个同类接口对象并赋给成员变量,那基本就是装饰器。
代理模式和装饰器在类图结构上有点像,但意图完全不同。代理模式强调“控制访问”,比如延迟加载、权限检查、日志记录。它的典型特征也是持有一个真实对象引用,但在调用真实方法前后夹带私活。软考对代理的考查不如适配器和装饰器频繁,但近两年出现过,值得了解。
组合模式同样值得留意,它最明显的标志是“树形结构 + 叶子与容器统一接口”。代码里会出现add()、remove()、getChild()这类方法,而且叶子节点和容器节点实现同一个接口。软考爱把它放在“文件目录”“组织架构”这类场景中。
2.3 行为型:策略、模板方法、观察者、状态
行为型模式是下午题六的“题库大户”。
策略模式的大概率出场方式,我已经在下一章用完整案例拆解。这里只说识别信号:上下文类里有一个策略接口类型的成员变量,通过setStrategy()之类的公开方法注入具体策略,调用时执行策略接口定义的方法。整个代码想表达的核心就是“算法可替换”。
模板方法模式的识别信号更简单:父类定义了一个完整的模板方法,方法内部按固定顺序调用几步操作,其中有些操作是抽象方法,留给子类实现。考试时常见场景是“员工入职流程”“报表生成流程”“考题生成流程”。出题人喜欢让你补父类模板方法里那行关键调用,或者补子类对抽象方法的实现。它和策略模式的区别在于:模板方法强调“骨架固定”,策略模式强调“算法整体替换”。
观察者模式的信号也非常明显:主题类里有一个集合成员变量,通常是List<Observer>,还有attach()、detach()、notify()三个方法。notify()里遍历所有观察者,调用各自的update()方法。考试场景多半是“股票价格变化通知”“群消息广播”“天气数据更新”。
状态模式经常和策略模式在结构上傻傻分不清。两者都是“把变化的行为封装成独立类”,但状态模式强调的是“对象内部状态变化导致行为变化”,而且具体状态类之间通常会互相流转。代码里常见一个setState()方法,上下文在状态类的方法中切换到另一个状态。软考如果考状态模式,题干里一定会有“状态转换”或“不同状态下行为不同”这类描述,抓住这一点去区分。
3. 一道真题风格的完整拆解
3.1 题干猜测与代码骨架
为了让你更直观地理解考场上的推理过程,我写一个高度贴近软考真题风格的完整案例,这道题融合了近几年策略模式考题的典型套路。
题目背景大概是这样的:某在线图书商城需要针对不同用户类型制定不同的折扣策略,目前有普通用户、学生用户和VIP用户三种。系统要求后期能方便地增加新的用户类型和折扣规则,且不影响已有代码。系统采用策略模式实现折扣计算,相关Java代码如下,其中标注了(1)到(6)六个空缺。
实际涉事代码会大致像下面这样:
java复制interface Discounter {
public double calculate(double price);
}
class NormalDiscounter implements Discounter {
public double calculate(double price) {
return price;
}
}
class StudentDiscounter implements Discounter {
private double rate;
public StudentDiscounter(double rate) {
this.rate = rate;
}
public double calculate(double price) {
return price * rate;
}
}
class VIPDiscounter implements Discounter {
public double calculate(double price) {
return price * 0.85;
}
}
class Book {
private double price;
private Discounter discounter;
public void setDiscounter(Discounter discounter) {
this.discounter = discounter;
}
public double getPrice() {
// 空缺(1)
}
public Book(double price) {
this.price = price;
}
}
题目第一小问会让你补全代码,第二小问让你写出代码采用的设计模式名称,第三小问通常围绕“为什么将折扣算法独立成类”“新增一种用户类型时如何扩展”这样的意图类问题展开。
3.2 缺口填充的推理全过程
拿到这种题,先别急着填。我的习惯是先把类之间的关系画出来,或者在心里过一遍角色分工。
这个案例里,Discounter接口是策略,三个具体实现类各自封装一种折扣规则。Book是上下文,它内部持有一个Discounter类型的引用,并通过setDiscounter()方法动态切换策略。现在要填getPrice()方法内部逻辑,这个方法的语义是“返回最终价格”,那它自然要委托给已设置好的discounter对象去计算。所以空缺(1)应该填:
java复制return discounter.calculate(price);
这个空是整道题的核心,考的其实不是设计模式,而是你能否读懂“委托调用”这么一层关系。很多考生在这里犹豫:要不要直接写price * 0.8?千万别。一旦这么写,Book类就和具体的折扣算法耦合死了,策略模式的整个意义就崩塌了。
除了这个核心空,出题人还经常在别的地方挖坑。比如让你补StudentDiscounter构造方法的参数类型,或者补VIPDiscounter里calculate方法的访问修饰符。这些小空考察的是Java基本功:接口方法默认是public abstract,实现类覆写时不能降低可见性,所以实现方法必须写public。这类知识点单独问你会,放进一大段代码里容易眼花,需要刻意提醒自己。
3.3 从题目看理论问答怎么答
第二小问问模式名称,这个纯粹看眼力。类图里有接口、有多个实现类、上下文持有接口引用并用set方法注入,这不是策略模式是什么?写答案时注意写全称“策略模式”,不要只写“Strategy”。有些考生用中文答题卡,模式名写英文也能给分,但稳一点就中文。
第三小问的答法有套路。问“为什么这么设计”时,我一般围绕三句话组织答案:第一句说可替换,第二句说可扩展,第三句说不违反开闭原则。
拿这道题举例,我会这样写:折扣算法被封装为独立策略类,运行时可以通过setDiscounter()随时替换,无需修改Book类代码;新增用户类型时,只需增加一个新的Discounter实现类,并将其实例注入即可,原有代码不需要改动,符合开闭原则。这三点写完,分数基本全拿。
理论题里还有一类变体考法:问你“这种模式与某某模式的区别”。比如策略模式和状态模式的区别、装饰器和代理模式的区别。这类题在下午题六里出现频率不算高,但近两年明显有上升趋势。答这类题的核心是抓住“目的差异”,而不是死记结构对比表。
4. 模式识别与答题技巧
4.1 类结构里的“报警信号”
与其一个模式一个模式地背,不如训练自己看代码结构猜模式的能力。我把高频模式的类结构信号整理成了一张表,备考时可以对照着反复看。
| 模式 | 最明显的类结构信号 | 关键方法关键字 |
|---|---|---|
| 简单工厂 | 一个工具类 + 一个静态创建方法,内部if-else | getInstance()、createXxx() |
| 工厂方法 | 抽象工厂接口 + 多个工厂子类,各自返回不同类型产品 | createProduct() |
| 单例 | 私有构造方法 + 静态自身实例 + 静态获取方法 | getInstance()、instance == null |
| 适配器 | 适配器类实现目标接口,内部引用被适配者 | adapter.method() |
| 装饰器 | 装饰器继承抽象组件并持有组件引用,方法内先调父类再增强 | super.operation() |
| 代理 | 代理类持有真实主题引用,方法内插入控制逻辑 | 调用真实类同名方法 |
| 组合 | 接口中同时有add/remove/getChild方法和业务方法 |
add()、getChild() |
| 策略 | 上下文持有策略接口引用,通过set方法注入 | setStrategy()、strategy.execute() |
| 模板方法 | 父类定义完整方法,内部调用抽象步骤方法,子类只实现步骤 | templateMethod() |
| 观察者 | 主题类中有观察者集合,notify遍历调update | attach()、notify()、update() |
| 状态 | 上下文有setState,状态类内部会切换为其他状态 | setState()、handle() |
这张表不是让你死记,而是让你在考场上形成“看到什么结构就往什么模式联想”的条件反射。举个例子,如果代码里大量出现getInstance()并且构造方法是private,那基本不用看第二眼,就是单例。
4.2 题干关键词与模式映射表
除了代码结构,题干文字里也有大量提示。软考出题人本质上很善良,他们怕你认不出模式,往往会用“可以自由切换”“动态增加”“通知所有”“模板流程固定”这类词来暗示你。我把常见的关键词和对应模式列一下。
如果题干说“运行时可以灵活更换算法”“避免多重条件判断”“算法相互独立”,那大概率是策略模式。如果题干说“流程步骤已经确定,但某些步骤的具体实现由子类决定”,那是模板方法模式。如果题干说“一对多依赖关系,一个对象变化时所有依赖对象收到通知”,那是观察者模式。如果题干说“在不修改现有类的前提下动态增加功能”,那是装饰器模式。如果题干说“使接口不兼容的类可以一起工作”,那是适配器模式。如果题干说“系统全局只需一个实例并全局访问”,那是单例模式。如果题干说“创建对象时希望由子类决定创建哪个对象”,那是工厂方法模式。
这些关键词在题干里出现的位置通常很显眼,一般是背景介绍那段话的后半部分。我建议你拿到试卷后,先花三十秒把题干通读一遍,圈出这类指向性词汇,再回头看代码,识别速度和准确率都会高不少。
还有一种更隐蔽的考法是:题干不直说“增加新算法”,而是说“新增一种用户类型”“新增一种支付方式”“新增一种报表格式”。只要看到“新增某类东西”这个句式,再对应到代码里的if-else判断或子类分发,基本就是工厂类模式或策略模式,具体看代码结构是“创建对象”还是“执行算法”。
4.3 理论问答万能组织逻辑
理论小题最怕答不到点子上。我总结了一个万能组织逻辑:先说“是什么”,再说“为什么”,最后说“怎么扩展”。
“是什么”用一句话概括模式的核心思想,比如“策略模式将一组可互相替换的算法封装起来”。网上背的那些定义太长,考场时间不够写,挑精炼的写。
“为什么”是得分重头戏,从解耦、扩展、可维护三个角度去靠。解耦就是看它把什么和什么分开了;扩展就是看新需求来的时候它怎么应对;可维护就是看它避免了什么坏味道,比如消灭了长if-else。
“怎么扩展”是让你举一个扩展例子,比如“新增VIP用户时,只需写一个VipDiscounter类并实现接口,再在客户端调用setDiscounter”。这个例子直接用题干场景里的名词,千万别写一个完全不相干的场景,阅卷人印象分会差。
这套逻辑可以套到几乎所有模式的理论问答上。你不需要背几十种不同答案,只需要背下来每个模式的一句话核心定义,剩下按这个框架现场组织就行。
5. 备考中容易踩的坑与考场细节
5.1 我见过最多的丢分原因
第一个坑是Java语法不熟导致“会但不对”。很多考生能看懂代码意图,也知道该填什么内容,但写出来总是差一点。比如接口实现方法的访问修饰符漏了public,抽象类子类漏了extends,接口漏了implements。这些错误在IDE里会被编译器当场拦截,到了手写答题纸上没有任何提醒,非常容易犯。我的习惯是每填一个类声明相关的空,都回头看一眼它上面是什么关键字环境,如果上面有interface,那下面一定是implements;上面有class,下面一定是extends。
第二个坑是忽略构造方法相关空位。软考的出题人特别喜欢在构造方法上做文章,让你补参数类型、补修饰符、补this赋值。比如VIPDiscounter类里有private double rate,构造方法里大概率就有this.rate = rate这行。这种空不看设计模式也能填出来,属于纯送分题,但很多考生只盯着大块的“模式代码”,反而把这种小空漏了。
第三个坑是理论小题答得太啰嗦。有些考生生怕说不全,一行接一行地写,结果写了八行还在绕圈子。阅卷时间有限,核心踩分句只要出现就行,写多了容易暴露错误。我建议理论题控制在三到四行,把“可替换、可扩展、开闭原则”这三个词点出来就够。
第四个坑比较隐蔽:模式名称写错别字或写混。比如“适配器”写成“配置器”,“装饰器”写成“装饰者”,“模板方法”写成“模板模式”。字错了阅卷人通常会扣分,所以平时默写的时候,就按真题答案的标准写法练,别按自己个人理解瞎缩写。
5.2 考场真实细节与时间控制
下午题六建议的答题时间控制在二十五分钟以内。拿到题先花两分钟通读背景描述和类图,圈出关键词,预测可能是什么模式。然后快速看一遍所有空位,把那些明显是“补类名、补接口名”的简单空先填掉,把需要推理的“调用代码”空留到后面重点想。这个顺序能保证不会因为最后时间紧张而丢掉白送分的小空。
还有一个考场细节容易被忽视:答题卡上的空号是(1)到(6),但在代码里可能跳着分布。填的时候一定要逐空对号,别跳行抄错。我见过一个朋友考完对答案,发现自己的六个空全部填对了,但答题卡上位置写串了一位,直接报废四个空,非常可惜。
手写代码时,类名、方法名最好和卷面上的一字不差。试卷里写的是getPrice,你就写getPrice,别临时给它“纠正”成getPrice()。括号在答题时可以加,但方法名大小写不能有丝毫偏差,Java对大小写敏感,阅卷也按字符串比对,错一个字母就是零分。
如果某个空实在不会填,不要留白。猜一个可能性最大的类名或方法名填上去,拿分的概率至少还有两三成。空着就一分没有,填错了,阅卷人如果觉得沾边也可能给一分。
6. 考前一周复习路线
6.1 七天复习路线表
如果你离考试还有一周多,可以按下面这个节奏安排,基本能把下午题六从“看过但不会”提到“稳定拿分”的状态。
- 第1天:把高频七个模式的类图结构过一遍,重点记每个模式的核心特征代码,不要求默写,能认出来就行。
- 第2天:动手抄写七个模式的最小Java骨架代码,每个模式大概二三十行,抄三遍,边抄边理解每行的作用。
- 第3天:开始刷真题,只刷2015年之后的下午题六部分。每道题都按“先识模式,再填代码,最后写理论答案”的完整流程走一遍。
- 第4天:继续刷真题,这次重点整理自己容易错的地方,是语法问题还是模式识别问题,分类记录。
- 第5天:合上资料,在纸上默写七个高频模式的代码骨架,每个模式默写到“看着类图能写出来”的程度。
- 第6天:做一套完整的下午真题模拟,时间控制在两小时以内,重点检验下午题六部分能不能在25分钟内完成。
- 第7天:只复习自己整理的问题清单和高频模式表,不再碰新题,保持手感和信心。
备考资料方面,真题是最好的素材。网上的软考真题资源良莠不齐,有些题目本身答案就有争议,我建议能对得到官方样卷或权威解析为准。另外,如果之前没有系统学过设计模式,可以先找一本讲设计模式入门的书快速看前几章,理解一下最基本的概念,再进入真题阶段会顺很多。
6.2 手写默写练习的正确姿势
预览模式的最小骨架时,不需要拿IDE跑,直接在纸上手写反而更贴近考场。每默写完一个模式,试着在自己旁边画一下类图,哪怕只是简单方框加箭头,都能帮助你建立“结构感”,对考场识别模式会格外有效。
举个例子,默写策略模式时,只要记住接口、三个实现类、一个持有接口引用的上下文;计算图时在笔记本上画出这个结构,填哪个空、为什么填,全都能对上了。
练习时特别注意几个容易手滑的地方:单例模式的getInstance()方法签名上面有没有static,模板方法模式里abstract void step()和void templateMethod()的位置有没有混淆,适配器里Adapter是implements Target而不是extends Target。这些细节在考试里就是那两三分。
7. 最后分享一点个人体会
设计模式这门东西,初学的时候觉得玄乎,刷完真题后你会发现,下午题六翻来覆去就是那么些组合。能把这十几分的到手方式想明白,它真的是软考下午卷里最“实惠”的一道题。
我自己的备考经历也验证了这一点。当时一起复习的朋友把大量时间投在算法题上,结果下午题六的模拟正确率一直在七成上下,上了考场反而因为时间分配不合理,最后只留了十几分钟给Java题。后来考前一周,我开始用这套“刷真题 + 默写骨架 + 画类图”的方法,保持着一种越做越顺手的感觉,最后下午综合成绩稳稳过线。
备考后期如果再有人问“下午题六难不难”,我通常就说一句话:别被“设计模式”这四个字吓住,它其实就是一套配合得很好的“填空游戏”。把高频模式熟悉到闭上眼睛能画出类图,这15分就能稳稳揣进口袋里。
