软件设计师这门考试里,第七章面向对象技术是很多人又爱又恨的一章。爱的是它不像算法、编译原理那样烧脑,概念读起来都认识;恨的是题目出得活,选择题里几个选项长得几乎一样,案例分析里画个类图、填个设计模式,稍不留神就丢分。我带过几轮备考,也自己啃过这一章,最大的感受是:面向对象技术不是背出来的,是“想明白”的。你得知道封装到底在封什么、多态在运行时怎么落地、UML那几种图各自解决什么问题,考场上才能一眼看穿出题人的意图。
这一章覆盖的内容其实很集中:面向对象的基本概念(封装、继承、多态、抽象)、UML各类图的识别与绘制、设计原则、以及二十多种设计模式的辨析。它既是上午选择题的常客,也是下午案例题里画图、填空、改错的主力。适合正在系统复习的考生,也适合那些概念都懂、一做题就错、想找一套可复现思路的人。下面我按自己复习和讲题的实际顺序,把这一章拆开讲透,重点放在“为什么这么设计”和“考场上怎么快速判断”上,而不是罗列定义。
1. 先搞清楚这一章到底在考什么
很多人复习第七章的方式是打开教材从头读到尾,读完感觉都懂,一做真题就懵。问题出在没有先建立“考点地图”。面向对象技术在软考里不是孤立的一章,它和后面的数据库设计、系统设计、甚至项目管理都有牵连,但就本章而言,考点的分布是有明显规律的。我把它拆成三块:概念辨析、UML建模、设计模式。这三块的出题风格完全不同,复习策略也得分开。
1.1 概念辨析:选择题的送分与送命区
概念部分看着简单,封装、继承、多态三个词谁都会说,但选择题偏偏在这里挖坑。比如问你“以下哪项体现了多态”,选项里会混进重载、重写、向上转型、接口实现,你得清楚多态的本质是“同一消息、不同响应”,而重载是编译期决定的静态绑定,严格说不算运行时多态。这类题我见过太多人栽跟头,原因就是背了定义没理解机制。
再比如封装,教材上说“隐藏内部实现细节,对外提供接口”,这话没错但太虚。考场上它会问你“封装的目的是什么”,选项里会有“提高运行效率”“减少代码量”“降低耦合”“方便继承”,正确答案往往落在降低耦合、提高可维护性上。你要是只记住“隐藏细节”,很容易被“提高安全性”这种似是而非的选项带偏。所以概念这块,我的建议是每个概念都追问一句:它解决了什么问题?没有它会怎样?这样理解下来,选项再花哨也能排除。
1.2 UML建模:下午案例题的重灾区
UML这部分,上午考图的认识,下午考图的绘制和填空。类图、用例图、顺序图、状态图、活动图,这五种是高频。很多人栽在类图上,因为类图里的关系太细:关联、聚合、组合、依赖、泛化、实现,六种关系箭头画法不同,含义也不同。考试不会让你默写定义,而是给你一段场景描述,让你判断两个类之间是什么关系,或者让你补全类图里的多重度。
我印象很深的一道真题,描述的是“一个订单包含多个订单项,订单项不能独立于订单存在”,问这是聚合还是组合。关键词是“不能独立存在”,这就是组合,生命周期绑定。如果描述是“一个部门包含多个员工,员工可以调到别的部门”,那就是聚合,生命周期独立。这种题没有捷径,就是把每种关系的判定关键词记牢,再配合箭头画法一起记。顺序图考的是消息传递的顺序和对象生命线,状态图考的是状态迁移的触发条件,活动图考的是并发和分支。每张图都有它“专属”的考点,复习时按图分类整理,比按章节顺序读效率高得多。
1.3 设计模式:最容易被低估的硬骨头
设计模式是第七章里分量最重、也最容易让人放弃的部分。二十三种模式,名字都记不全,更别说区分了。但我实际复习下来发现,考试真正高频的也就十来个:单例、工厂方法、抽象工厂、建造者、原型、适配器、桥接、组合、装饰器、代理、观察者、策略、模板方法、状态、命令。剩下的要么很少考,要么考也是送分题。
关键在于,设计模式不能按名字背,要按“它解决什么问题”来归类。创建型模式解决对象创建的问题,结构型解决类与对象的组合问题,行为型解决对象间的交互和职责分配问题。每一类里再细分,比如同样是创建型,工厂方法关注“一个产品等级”,抽象工厂关注“多个产品族”,建造者关注“复杂对象的构建过程与表示分离”,原型关注“通过复制创建”。你把它们的意图摆在一起对比,差异就出来了。下午案例题里常考的是“根据场景选模式”和“补全模式中的类”,前者靠意图匹配,后者靠记住模式的典型类结构。
2. 封装继承多态:把三个词讲透比背十遍强
这一节我想把面向对象三大特性掰开揉碎讲。不是重复教材,而是讲考场上怎么用、怎么辨。很多人复习到这觉得太基础,扫一眼就过,结果选择题错的就是这些“基础”。我吃过这个亏,后来专门花时间把每个特性的“边界”理清楚,正确率才稳下来。
2.1 封装:不只是加个private
封装的字面意思是把数据和操作数据的方法包在一起,隐藏内部实现。但考试考的是它的目的和效果。我总结封装的本质是“控制访问边界”:哪些东西对外可见,哪些只能内部用,由设计者决定。这样做的好处是修改内部实现时不影响外部调用者,也就是降低耦合。你想想,如果一个类的属性全公开,外部代码直接改它,哪天你改了属性的计算逻辑,所有外部代码都得跟着改,这就是高耦合。封装把这种依赖切断,外部只能通过你提供的方法访问,你改内部逻辑,方法签名不变,外部就不受影响。
选择题里常见的干扰项是“封装提高程序运行效率”。这是错的,封装不直接提升效率,甚至可能因为多一层方法调用略微降低效率,它提升的是可维护性和安全性。还有“封装实现代码复用”,复用主要是继承的功劳,封装不直接带来复用。把这两条记清楚,这类题基本不会错。另外要注意,封装在UML里对应的是类图中属性和方法的可见性标记:-表示private,+表示public,#表示protected,~表示包内可见。下午题让你补全类图时,可见性符号写错是要扣分的。
2.2 继承:is-a关系的双刃剑
继承表达的是“是一个”的关系,子类继承父类的属性和方法,同时可以扩展或覆盖。考试里考继承,一是考UML里的泛化关系(空心三角箭头指向父类),二是考继承带来的问题。继承最大的问题是破坏封装:子类能访问父类的protected成员,父类改实现,子类可能受影响,这就是所谓的“ fragile base class”问题。所以设计原则里有一条“优先使用组合而非继承”,原因就在这。
选择题会问你“以下关于继承的说法正确的是”,选项里常有“继承提高了代码的耦合度”这种。注意,继承确实增加了子类和父类之间的耦合,这是它的代价,换来的是复用和多态的基础。所以这句话本身是对的,别一看“耦合度高”就以为是错的。还有一道经典题问“子类可以继承父类的哪些成员”,答案是public和protected,private成员子类能继承但无法直接访问,这个细节要记牢。下午题里如果让你画继承关系,箭头方向千万别画反,空心三角永远指向父类。
2.3 多态:运行时才见分晓
多态是三大特性里最抽象、也最容易考深的。它的定义是“同一操作作用于不同对象,产生不同结果”。实现多态的前提有三个:继承、方法重写、父类引用指向子类对象。缺一不可。考试里最爱考的是区分“重载”和“重写”。重载是同一个类里方法名相同、参数列表不同,编译期就确定调用哪个,属于静态绑定;重写是子类重新定义父类的方法,方法签名相同,运行时根据实际对象类型决定调用哪个,属于动态绑定。多态指的是后者。
我见过一道题,给了一段代码,父类引用指向子类对象,调用一个被子类重写的方法,问输出什么。这种题只要记住“看实际对象类型,不看引用类型”就不会错。但如果调用的方法没有被子类重写,那就调用父类的实现。还有一种情况是父类引用调用子类特有的方法,编译直接报错,因为编译器只看引用类型。这些细节在选择题里反复出现,建议拿几段小代码自己推一遍,比看定义管用。多态在UML里没有专门的图,但它体现在类图的继承关系和顺序图的消息传递中,理解多态对读懂顺序图很有帮助。
3. UML图逐个击破:认图、画图、填图
UML是第七章下午题的主战场,也是很多人失分的地方。我把高频的五种图按“考什么、怎么认、怎么画”三个维度整理。这里先给一个总的原则:UML图不是画得越全越好,考试只考它关心的那部分,所以复习时抓每种图的核心元素,别被边角知识拖累。
3.1 类图:六种关系是命门
类图是UML里最重要、考得最多的图。它的核心是类与类之间的关系。六种关系我按耦合从弱到强排:依赖、关联、聚合、组合、泛化、实现。依赖是最弱的,一个类的方法参数或局部变量用到了另一个类,用虚线箭头表示。关联是类之间的结构关系,用实线表示,可以带多重度。聚合是整体与部分的关系,部分可以独立于整体存在,用空心菱形表示,菱形在整体一端。组合也是整体与部分,但部分不能独立存在,用实心菱形表示。泛化就是继承,空心三角指向父类。实现是类实现接口,虚线加空心三角。
判定关系的关键是看描述里的“生命周期”和“强弱”。我整理了一个速查表,考场上对着套:
| 关系 | 符号 | 判定关键词 | 耦合强度 |
|---|---|---|---|
| 依赖 | 虚线箭头 | 临时使用、方法参数 | 最弱 |
| 关联 | 实线 | 长期结构关系、成员变量 | 弱 |
| 聚合 | 空心菱形 | 整体部分、部分可独立 | 中 |
| 组合 | 实心菱形 | 整体部分、部分不可独立 | 强 |
| 泛化 | 空心三角 | 继承、is-a | 强 |
| 实现 | 虚线空心三角 | 实现接口 | 强 |
多重度也是常考点,1表示一个,0..1表示零或一个,*表示零或多个,1..*表示一个或多个。下午题常让你根据描述填多重度,比如“一个订单包含一个或多个订单项”,订单端写1,订单项端写1..*。注意多重度标在关联线的两端,表示对方类的实例数量,方向别搞反。
3.2 用例图:参与者与用例的边界
用例图考的是系统边界和功能划分。核心元素是参与者(Actor)、用例(Use Case)、以及它们之间的关系:关联、包含、扩展、泛化。参与者是人或外部系统,用例是系统提供的功能。包含关系(include)表示一个用例必然用到另一个用例,比如“下单”必然包含“验证库存”。扩展关系(extend)表示在特定条件下才执行的用例,比如“下单”可以扩展“使用优惠券”。这两个关系箭头方向容易混:包含是箭头指向被包含的用例,扩展是箭头指向被扩展的用例(也就是基础用例)。
选择题常考“以下哪种关系表示可选行为”,答案就是扩展。下午题可能让你根据描述补全用例图,关键是找准参与者和用例,别把系统内部的处理步骤当成用例。用例应该是用户视角的功能,粒度不要太细。
3.3 顺序图与状态图:一个管交互,一个管变迁
顺序图考的是对象之间消息传递的时间顺序。核心元素是对象(顶部方框)、生命线(垂直虚线)、激活条(矩形)、消息(水平箭头)。同步消息是实心箭头,异步消息是开放箭头,返回消息是虚线箭头。考试常考消息的顺序和对象的创建销毁。读顺序图时从上往下看,箭头方向就是调用方向。
状态图考的是单个对象的状态变迁。核心元素是状态(圆角矩形)、迁移(箭头)、事件(箭头上的文字)、初始状态(实心圆)、终止状态(同心圆)。常考的是根据描述判断状态迁移的触发事件,或者补全缺失的状态。状态图的关键是找准“状态”和“事件”,一个状态在某个事件触发下迁移到另一个状态。注意状态和动作的区别,状态是对象所处的状况,动作是状态内执行的操作。
4. 设计模式:按意图归类,按场景匹配
设计模式这一块,我前面说了不能按名字背。这一节我按创建型、结构型、行为型三类,把高频模式的意图、典型结构和考场判断方法讲清楚。每个模式我都配一个“考场识别信号”,也就是题目描述里出现什么词,你就该想到它。
4.1 创建型模式:管对象怎么生
创建型模式解决对象创建的问题,把创建和使用分离。高频的有五个:单例、工厂方法、抽象工厂、建造者、原型。
单例模式的意图是保证一个类只有一个实例,并提供全局访问点。考场识别信号是“唯一实例”“全局访问”“资源管理器”。典型结构是一个私有构造方法、一个静态私有实例、一个静态公有获取方法。选择题常考“单例模式的实现要点”,注意多线程环境下要考虑同步。
工厂方法模式的意图是定义一个创建对象的接口,让子类决定实例化哪个类。识别信号是“一个产品等级”“子类决定创建”。抽象工厂是“多个产品族”“一系列相关产品”。这两个容易混,记住工厂方法管一个维度,抽象工厂管多个维度。建造者模式识别信号是“复杂对象的构建过程与表示分离”“分步骤构建”。原型模式识别信号是“通过复制创建”“克隆”。
| 模式 | 意图关键词 | 考场信号 |
|---|---|---|
| 单例 | 唯一实例、全局访问 | 资源管理、配置 |
| 工厂方法 | 子类决定创建 | 一个产品等级 |
| 抽象工厂 | 创建产品族 | 多个相关产品 |
| 建造者 | 构建与表示分离 | 分步骤、复杂对象 |
| 原型 | 复制创建 | 克隆、拷贝 |
4.2 结构型模式:管对象怎么组
结构型模式解决类与对象的组合。高频的有适配器、桥接、组合、装饰器、代理。
适配器模式的意图是转换接口,让原本不兼容的类能一起工作。识别信号是“接口不兼容”“转换”“复用现有类”。桥接模式的意图是把抽象部分与实现部分分离,使它们可以独立变化。识别信号是“两个维度独立变化”“避免类爆炸”。组合模式的意图是把对象组合成树形结构,表示整体部分层次。识别信号是“树形结构”“整体部分一致对待”。装饰器模式的意图是动态给对象添加职责。识别信号是“动态扩展功能”“不改变原类”。代理模式的意图是为其他对象提供代理以控制访问。识别信号是“控制访问”“间接层”。
适配器和装饰器容易混,适配器是改接口,装饰器是加功能。桥接和组合也容易混,桥接是分离两个变化维度,组合是树形结构。这些差异在选择题里反复考,建议用一句话概括每个模式的核心,背下来。
4.3 行为型模式:管对象怎么交互
行为型模式解决对象间的职责分配和交互。高频的有观察者、策略、模板方法、状态、命令。
观察者模式的意图是定义一对多依赖,当一个对象状态改变时,所有依赖它的对象都得到通知。识别信号是“一对多”“状态改变通知”“订阅发布”。策略模式的意图是定义一系列算法,把它们封装起来,使它们可以互相替换。识别信号是“多种算法可互换”“运行时选择算法”。模板方法的意图是定义算法骨架,将某些步骤延迟到子类。识别信号是“算法骨架固定”“部分步骤可变”。状态模式的意图是允许对象在内部状态改变时改变它的行为。识别信号是“状态驱动行为”“状态迁移”。命令模式的意图是把请求封装成对象。识别信号是“请求封装”“撤销重做”“队列请求”。
策略和状态容易混,策略是算法可替换,状态是行为随状态变。模板方法和策略也容易混,模板方法是骨架固定、步骤可变,策略是整体算法可替换。这些辨析在下午案例题里常以“根据场景选择合适模式”的形式出现,选错就丢分。
5. 实操演练:一道案例题的完整拆解
光说不练假把式。这一节我拿一道典型的下午案例题,从读题到画图到填空,完整走一遍。题目是虚构的,但结构和真题一致。场景:某图书管理系统需要支持图书借阅、归还、查询,借阅时需验证读者资格和图书库存,归还时计算逾期罚金。要求画出类图并说明关系。
5.1 读题与识别类
第一步是找名词。读者、图书、借阅记录、罚金规则,这些是候选类。读者有属性借书证号、姓名,方法借书、还书。图书有属性ISBN、书名、库存,方法查询库存、更新库存。借阅记录有属性借阅日期、归还日期,方法计算逾期天数。罚金规则有方法计算罚金。注意“验证读者资格”和“验证图书库存”是借阅这个动作里的步骤,不单独成类,除非题目明确要求。
识别类的时候要克制,别把每个动词都变成类。类应该是名词,且是有状态和行为的实体。像“验证”这种动作,通常是某个类的方法。这一步的常见错误是把系统功能当类,比如“借阅管理”不是类,它是用例。
5.2 确定关系与多重度
读者和借阅记录是一对多,一个读者可以有多条借阅记录,一条记录属于一个读者。图书和借阅记录也是一对多,一本书可以被多次借阅,一条记录对应一本书。借阅记录和罚金规则是依赖,计算罚金时用到罚金规则。读者和图书之间通过借阅记录关联,不直接关联。
多重度这样标:读者端1,借阅记录端0..*;图书端1,借阅记录端0..*。注意借阅记录是关联类,它连接读者和图书,同时有自己的属性。如果题目要求,可以把借阅记录画成关联类,用虚线连到关联线上。
5.3 补全类图与检查
画完类图后检查三件事:关系符号对不对、多重度方向对不对、可见性标记有没有漏。关系符号上,读者和借阅记录是关联,实线;图书和借阅记录是关联,实线;借阅记录和罚金规则是依赖,虚线箭头。多重度标在线的两端,靠近对方类。可见性上,属性一般private,方法public。
这道题如果出现在考场上,还可能让你补全某个方法的参数或返回值,或者让你说明为什么用某种关系。答题时注意用词准确,比如“组合”和“聚合”别写混,“泛化”别写成“继承”(虽然意思对,但UML术语要用泛化)。
6. 踩过的坑与考场技巧
复习这一章,我踩过的坑不少,有些是概念没吃透,有些是考场策略问题。这一节把最有价值的几条经验整理出来,都是实打实影响分数的。
6.1 概念题的三个高频陷阱
第一个陷阱是把重载当多态。前面说过,重载是静态绑定,多态是动态绑定,选择题里经常用重载来冒充多态,看到“参数列表不同”就要警惕。第二个陷阱是混淆聚合和组合。记住“生命周期是否绑定”这一条就够了,部分能独立存在是聚合,不能独立是组合。第三个陷阱是可见性符号记错。-是private,+是public,#是protected,~是包内可见,这四个符号在类图题里必须写对,写错直接扣分。
还有一个容易忽略的点:抽象类和接口的区别。抽象类可以有构造方法、可以有具体方法,接口在传统UML里只有抽象方法(新版本可以有默认方法,但考试一般按传统理解)。选择题会问“以下关于抽象类和接口的说法正确的是”,注意抽象类体现is-a,接口体现can-do。
6.2 下午题的答题节奏
下午题时间紧,案例题里UML部分不能耗太久。我的策略是先读问题再读题干,带着问题找信息。比如问“补全类图”,就先看类图缺什么,再回题干找对应描述。画图时先用铅笔(考场允许的话)或先在草稿上画,确认关系再誊写。多重度和关系符号是扣分重灾区,誊写时逐条核对。
还有一点,答题要写UML术语,别用大白话。问关系就答“关联”“聚合”“组合”“泛化”“依赖”“实现”,别答“包含”“属于”这种模糊词。问模式就答模式名,别描述功能。阅卷是按关键词给分的,术语准确很重要。
6.3 设计模式的记忆方法
二十三种模式硬背不现实,我的方法是“三遍法”。第一遍按分类过一遍,知道每类有哪些模式。第二遍按意图对比,把容易混的成对记,比如工厂方法vs抽象工厂、适配器vs装饰器、策略vs状态。第三遍做真题,每道题不管对错,都把涉及的模式意图复述一遍。三遍下来,高频模式基本就刻在脑子里了。
另外,下午题选模式时,先看题目描述里的“变化点”。如果变化的是算法,选策略;变化的是状态,选状态;变化的是创建的产品,选工厂;变化的是功能扩展,选装饰器。抓住变化点,模式就选对了一半。
7. 复习路线与资料取舍
最后说说复习安排。这一章内容多,但不能平均用力。我的建议是分三轮。第一轮通读教材,建立概念框架,重点理解三大特性和UML五种图。第二轮专项突破,把设计模式按分类对比记忆,配合真题练习。第三轮查漏补缺,把错题涉及的知识点回炉,重点看易混点和易错符号。
资料方面,教材是基础,但教材的设计模式部分写得比较散,建议配合一份自己整理的对比表。真题是最好的练习材料,近五年的真题至少做两遍,第一遍按章节做,第二遍按套卷做。做题时注意总结出题人的套路,比如类图题爱考哪种关系、模式题爱考哪几个模式,总结出来就是你的得分点。
我个人在实际操作中的体会是,这一章不怕慢,就怕浮。概念看似简单,但每个概念背后都有设计思想,理解了思想,题目怎么变都不慌。设计模式看似多,但归了类、对了比,真正要记的就那么十几个。UML图看似杂,但每种图抓核心元素,画图时逐项核对,失误就少。复习到后期,我习惯拿一张白纸默画类图的六种关系符号,默写高频模式的意图关键词,这种主动回忆比反复看书有效得多。