ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

软件设计师面向对象技术:UML图解法与设计模式识别

软件设计师面向对象技术:UML图解法与设计模式识别

1. 为什么面向对象这一章值得单独拎出来死磕

软件设计师考试里,第七章面向对象技术是个很特殊的存在。它不像数据结构那样需要你现场推演算法,也不像操作系统那样概念密集到让人头皮发麻,但它有个更阴险的特点——分值分布广、题型跨度大、上午选择题和下午大题两头都占。我翻过近七八套真题,上午场里面向对象相关的题目稳定在6到8分,下午场的数据流图、UML图、设计模式填空加起来能占到15分往上。换句话说,这一章你要是拿不稳,上午可能刚好卡在及格线边缘,下午直接丢掉一道大题。

很多人备考时的误区是把这章当成“背概念”来处理,封装继承多态背得滚瓜烂熟,一到UML类图题就懵。问题出在哪儿?面向对象技术的考试重点从来不是让你复述定义,而是考你能不能把现实问题翻译成对象模型。上午题考的是你对UML图元素、关系符号、设计模式特征的识别能力,下午题考的是你从一段文字描述里提取类、属性、方法,再画出类图或者填出缺失代码的能力。这两件事的底层能力是同一个:对象建模的思维习惯。

我当初备考的时候,这一章前后过了三遍。第一遍通读教材,感觉什么都懂;第二遍做真题,发现UML关联关系里的聚合和组合永远分不清;第三遍才摸到门道——原来这章的核心不是记忆,是建立一套从需求文字到类图元素的映射规则。这套规则一旦建立起来,上午题扫一眼就能排除错误选项,下午题看到“包含”“由……组成”“管理”这类动词就能条件反射地判断关系类型。

这篇内容适合两类人看:一类是刚翻开教材、对面向对象只有模糊印象的零基础备考者,另一类是已经刷过题但正确率卡在60%上下、需要把知识体系重新捋一遍的老考生。我会把这一章拆成概念底座、UML图解法、设计模式识别、下午题实操套路四个板块,每个板块都配上我实际做题时总结的判断口诀和踩坑记录。你不需要有编程经验,但需要准备好纸笔,因为下面很多内容是要你跟着画的。

2. 概念底座:把封装继承多态从口号变成判断依据

2.1 封装到底在考什么:信息隐藏的边界感

教材上对封装的经典定义是“把数据和操作数据的方法捆绑在一起,并隐藏对象的内部实现细节”。这句话你背十遍不如做一道题来得实在。考试里考封装,从来不会直接问你“什么是封装”,而是给你一个类图,问你这个类的属性前面加了下划线或者减号代表什么,或者问你某个方法应该设为public还是private。

我自己的理解方式是这样的:封装考的是你对“可见性边界”的敏感度。UML类图里属性和方法前面那些符号——+是public,-是private,#是protected,~是包内可见——这些符号的用法是有套路的。属性绝大多数情况应该是-,因为数据要藏起来;方法则要看它是不是对外提供的服务接口,是的话用+,只给自己内部调用的辅助方法用-。

这里有个我踩过的坑:下午题里经常出现“某类负责计算总价”这种描述,很多人会把计算方法设成public,但实际上如果这个计算只是被另一个类内部调用,它应该是private的。判断依据很简单——看这个方法有没有被其他类的对象直接调用。如果没有,就藏起来。考试出题人特别喜欢在“辅助方法”上设陷阱,你稍微不注意就把可见性标错了。

实操心得:做类图题时,先把所有属性一律标成-,再逐个检查方法。只有那些在题目描述里明确出现“供外部使用”“对外提供”“可以被调用”字眼的方法才标+,其余全部-。这个粗暴规则能帮你避开八成以上的可见性错误。

2.2 继承与多态的联合考法:父子类替换的逻辑

继承单独考的时候很简单,就是箭头指向父类、子类继承父类的属性和方法。但考试真正爱考的是继承和多态绑在一起的场景。典型题目会给你一个父类“图形”和子类“圆形”“矩形”,父类有个方法“计算面积”,然后问你运行时调用哪个方法。

这里的关键知识点是动态绑定。很多零基础的人不理解为什么要有这个东西,我用一个生活化的类比来解释:你去餐厅点菜,菜单上写的是“招牌菜”,但每家分店做出来的招牌菜不一样。菜单就是父类接口,分店就是子类实现,你点菜这个动作在运行时才决定到底吃的是什么。考试里考多态,就是考你能不能判断出“声明类型”和“实际对象类型”不一致时,方法调用跟着谁走。

上午题里常见的坑是给你一段伪代码,父类引用指向子类对象,然后调用一个父类有、子类也重写了的方法,问你输出什么。答案永远是子类重写后的版本。但如果子类没有重写,那就走父类的实现。这个判断链条要练到条件反射。

下午题里多态考得更隐蔽。题目描述里会说“系统需要支持多种支付方式,且未来可能增加新的支付方式”,这就是在暗示你用多态——定义一个支付接口,各种支付方式实现它,调用方只依赖接口不依赖具体实现。你看到“未来可能增加”“支持多种类型”“可扩展”这类词,脑子里就要亮灯:这里要留抽象类或接口。

2.3 抽象类与接口的取舍:考试里的判断信号

抽象类和接口的区别是上午选择题的高频考点,但很多人背了七八条区别还是不会做题。我总结了一个极简判断法:看题目里有没有“默认实现”的需求。如果题目说“所有子类共享某段逻辑,但又有各自不同的行为”,那就用抽象类,因为抽象类可以有方法体;如果题目说“只定义规范,不关心怎么实现”,那就用接口。

考试里还有一个隐藏信号:看类之间的关系强度。如果两个类之间是“is-a”的关系,比如“经理是一种员工”,用继承(抽象类);如果是“can-do”的关系,比如“可序列化”“可比较”,用接口。这个判断法在下午题填类图时特别管用,因为题目描述里往往不会明说用哪个,你得从语义里抠。

我备考时整理过一张对比表,做题时直接对照:

判断维度选抽象类选接口
是否有共享代码有公共方法体只有方法签名
关系语义is-a(是一种)can-do(能做什么)
子类约束单继承,强约束多实现,弱约束
题目关键词“共享”“通用逻辑”“基类”“规范”“协议”“能力”

这张表不是让你背的,是让你在做题时快速定位的。看到“共享”找抽象类,看到“能力”找接口,基本不会错。

3. UML图解法:六种关系一张图理清楚

3.1 依赖、关联、聚合、组合的箭头密码

UML类图里的关系判断是上午题的送分题,也是下午题的丢分重灾区。四种关系——依赖、关联、聚合、组合——画法不同、语义不同,但题目描述往往很模糊。我当初在这上面栽过跟头,后来总结了一套“从弱到强”的判断顺序,做题时按顺序套,准确率能到九成以上。

先看强弱排序:依赖 < 关联 < 聚合 < 组合。依赖是最弱的,临时性的,一个类的方法里用到了另一个类;关联是长期的,一个类持有另一个类的引用;聚合是整体和部分的关系,但部分可以独立存在;组合也是整体和部分,但部分不能独立存在,生命周期绑定。

画法上的区别必须记牢:依赖是虚线箭头,关联是实线箭头(或者实线无箭头),聚合是空心菱形加实线,组合是实心菱形加实线。考试里经常把聚合和组合的菱形画反,或者把依赖和关联的线型搞混。

我用一个记忆口诀:“虚依实关,空聚实组”。虚线是依赖,实线是关联;空心菱形是聚合,实心菱形是组合。这个口诀我写在便利贴上贴了两个月,做题时扫一眼就能对上。

题目描述里的关键词对应关系是这样的:

  • 出现“使用”“调用”“依赖” → 依赖
  • 出现“拥有”“包含”“关联” → 关联
  • 出现“由……组成”“整体部分”且部分可独立 → 聚合
  • 出现“不可分割”“同生共死”“完全属于” → 组合

注意:聚合和组合的判断是下午题的高频失分点。我见过一道真题描述“一个部门包含多名员工”,很多人画成组合,但员工离职后部门还在,员工也可以调到别的部门,所以是聚合。判断标准就一条——部分对象能不能脱离整体单独存在。能,就是聚合;不能,就是组合。

3.2 泛化与实现的区分:箭头方向别画反

泛化就是继承,画法是实线加空心三角箭头,箭头指向父类。实现是类实现接口,画法是虚线加空心三角箭头,箭头指向接口。这两个的箭头都是空心三角,区别在线型:实线是泛化,虚线是实现。

考试里最爱考的是箭头方向。很多人画反,把箭头指向子类。记住一个原则:箭头永远指向更抽象的一方。子类比父类具体,所以箭头从子类指向父类;实现类比接口具体,所以箭头从实现类指向接口。这个原则想通了,方向就不会错。

下午题里泛化和实现经常混在一起考。比如题目描述“系统中有多种报表,所有报表都需要实现导出功能,但每种报表的导出格式不同”,这里“多种报表”之间如果有共性,可能有一个抽象基类(泛化),而“导出功能”如果定义成接口,就是实现关系。你得从描述里拆出两层抽象:类层次的继承用泛化,行为规范的实现用接口。

3.3 类图元素速查:从类名到多重度

类图题里除了关系,还有几个细节容易丢分。第一个是类的 stereotypes,也就是<<interface>>、<<abstract>>这些标记。题目如果说“定义了一个接口”,类名上面必须加<<interface>>,不写要扣分。第二个是多重度,也就是关联两端的数字,比如1、0..*、1..*。题目描述里出现“一个订单包含多个商品”,订单端标1,商品端标0..*或者1..*,具体看描述里有没有“至少一个”的意思。

第三个是属性名的命名规范。考试不强制要求驼峰命名,但属性名要能反映含义,不能写a、b、c。我一般用题目里出现的名词直接当属性名,比如“书名”“价格”“库存数量”,这样既不会错也不会漏。

我整理了一张类图元素检查清单,做完题后逐项打勾:

  • [ ] 类名是否与题目描述一致
  • [ ] 抽象类和接口是否加了构造型标记
  • [ ] 属性和方法的可见性符号是否正确
  • [ ] 关系类型(依赖/关联/聚合/组合/泛化/实现)是否判断准确
  • [ ] 箭头方向是否指向抽象方
  • [ ] 多重度是否标注且与描述吻合
  • [ ] 方法名是否体现了职责

这张清单看着琐碎,但下午题一道类图填空动辄十几分,丢一个细节可能就是两三分的事。

4. 设计模式识别:二十三种模式的考试筛法

4.1 按目的分类:创建型、结构型、行为型

设计模式二十三种,全背下来不现实,但考试有考试的筛法。上午题考设计模式,基本只考模式的特征识别——给你一段场景描述,问你该用哪个模式,或者给你一个类图,问你这是哪个模式。下午题考得更深一点,可能让你填某个模式的缺失代码。

我的策略是按目的分三组,每组记三到四个高频模式。创建型里高频的是单例、工厂方法、抽象工厂、建造者;结构型里高频的是适配器、装饰器、代理、组合;行为型里高频的是观察者、策略、模板方法、命令、状态。这十几个模式覆盖了历年真题的绝大多数考点,剩下的低频模式考前扫一眼特征就行。

创建型模式的共同特征是把对象的创建过程封装起来。题目里出现“希望只创建一个实例”就是单例,“根据参数决定创建哪种对象”就是工厂,“创建过程复杂、分步骤”就是建造者。结构型模式的共同特征是处理类和对象的组合。出现“接口不兼容、需要转换”是适配器,“动态添加功能”是装饰器,“控制访问”是代理。行为型模式的共同特征是处理对象间的通信和职责分配。出现“一对多依赖、状态变化通知”是观察者,“多种算法可互换”是策略,“固定流程、部分步骤可变”是模板方法。

4.2 高频模式的特征速记与易混辨析

单例模式的识别信号最明显:私有构造方法 + 静态私有实例 + 静态公有获取方法。类图里看到构造方法是-,有一个静态的自身类型属性,基本就是单例。考试里常考的是懒汉式和饿汉式的区别,懒汉式在获取方法里判断实例是否为空再创建,饿汉式在类加载时就创建。题目如果说“第一次使用时才创建”,选懒汉式。

工厂方法和抽象工厂容易混。工厂方法是一个工厂生产一种产品,抽象工厂是一个工厂生产一族产品。判断信号:题目说“一个工厂类负责创建一种产品”是工厂方法,说“一个工厂类负责创建多个相关产品”是抽象工厂。我记的口诀是:“单产品找工厂方法,产品族找抽象工厂”。

适配器和装饰器也容易混。适配器是改变接口,让原本不兼容的两个东西能合作;装饰器是增强功能,在不改变接口的前提下加新行为。题目里出现“已有类接口不符合需求”是适配器,出现“动态地给对象添加职责”是装饰器。

观察者和策略的区分看通信方向。观察者是主题状态变化后主动通知多个观察者,是一对多的推送;策略是客户端选择一种算法交给上下文执行,是主动的选择。题目说“状态改变时自动通知”是观察者,说“运行时切换算法”是策略。

我整理了一张高频模式速查表,考前反复看:

模式识别关键词类图特征
单例唯一实例、全局访问私有构造、静态自身属性
工厂方法一种产品、子类决定创建工厂接口+产品接口
抽象工厂产品族、一系列相关产品工厂接口含多个创建方法
适配器接口转换、兼容适配器实现目标接口、持有源对象
装饰器动态添加功能装饰器实现组件接口、持有组件引用
代理控制访问、间接调用代理实现主题接口、持有真实主题
观察者通知、一对多、状态变化主题持有观察者列表
策略算法互换、运行时选择上下文持有策略接口

4.3 下午题里的模式填空套路

下午题考设计模式,通常是给你一段代码框架,让你填缺失的类名或者方法名。这种题的套路是先判断模式,再套模板。判断模式靠题目描述里的关键词,套模板靠你对这个模式标准结构的熟悉程度。

比如题目描述“系统需要支持多种日志记录方式,且可以在运行时切换”,这是策略模式。标准结构是:一个策略接口定义日志方法,多个具体策略实现它,一个上下文类持有策略接口的引用并提供切换方法。填空时你只需要把题目里的具体类名往这个结构里套。

再比如“系统需要在数据变化时自动更新多个界面组件”,这是观察者模式。标准结构是:一个主题接口定义注册、注销、通知方法,一个具体主题维护观察者列表,一个观察者接口定义更新方法,多个具体观察者实现它。填空时注意主题里的观察者列表类型是观察者接口的集合,不是具体观察者。

实操心得:下午题的模式填空,先把题目里出现的所有类名列出来,然后对照标准模式结构,看哪个类对应接口、哪个对应具体实现、哪个对应上下文。对应关系理清了,填空就是体力活。我做过一道真题,题目给了五个类名,标准观察者结构需要四个角色,多出来的那个是干扰项,你得能识别出来它不属于模式结构。

5. 下午题实操:从需求文字到类图的完整推演

5.1 需求描述的信息提取方法

下午题第一道大题通常是数据流图,第二道是数据库设计,第三道就是UML类图或者设计模式。类图题的题干一般是一段业务描述,两三百字,里面藏着类、属性、方法和关系。我的提取方法是三遍阅读法。

第一遍通读,圈出所有名词。名词大概率是类或者属性。比如“系统管理图书信息,图书包含书名、作者、ISBN”,这里“图书”是类,“书名”“作者”“ISBN”是属性。第二遍通读,圈出所有动词。动词大概率是方法或者关系。比如“管理员可以添加图书”,这里“添加”是方法,“管理员”和“图书”之间可能有关联。第三遍精读,找数量词和限定词。“一个订单包含多个商品”里的“一个”和“多个”就是多重度,“必须”“至少”这些词影响的是约束条件。

三遍读完,你手里应该有一张草稿:左边列类名,右边列属性和方法,中间画关系连线。这时候再往答题卡上誊写,基本不会漏。

我踩过的一个坑是把属性当成了类。题目说“图书有出版社信息”,很多人会把“出版社”单独拎出来当一个类,但如果题目没有进一步描述出版社的属性,它就应该作为图书的一个属性(出版社名称)处理。判断标准:看题目有没有对这个名词做进一步展开描述。展开了就是类,没展开就是属性。

5.2 类图绘制的标准流程与检查点

画类图的标准流程我总结为五步:定类、定属性、定方法、定关系、定多重度。这五步顺序不能乱,因为后一步依赖前一步的结果。

定类的时候,优先找题目里的核心业务实体。比如图书管理系统,核心实体是图书、读者、借阅记录。辅助类比如“数据库连接类”如果题目没提就不要加。定属性的时候,每个类至少要有两到三个属性,属性名从题目描述里直接摘。定方法的时候,注意题目里描述的动作归属哪个类——“管理员添加图书”这个动作,方法应该放在管理员类还是图书类?答案是看谁执行这个动作。管理员执行添加,方法在管理员类;但如果题目说“图书信息被更新”,更新方法可能在图书类。

定关系是重头戏。我一般先画泛化和实现(如果有),再画组合和聚合,最后画关联和依赖。因为强关系优先确定,弱关系后补。多重度标注时,注意关联两端的数字要成对出现,不能只标一端。

画完之后对照检查清单过一遍:类名有没有遗漏、属性可见性是不是都标了-、方法可见性有没有区分、关系类型对不对、箭头方向对不对、多重度标了没有。这套流程走下来,一道15分的类图题拿12分以上是稳的。

5.3 代码填空题的上下文推断技巧

下午题里还有一种题型是给出一段Java或者C++代码框架,让你填缺失的部分。这种题考的是你对面向对象语法和设计模式结构的双重理解。

推断技巧分三步。第一步看类名和接口名,判断这是什么模式。类名里有“Factory”就是工厂相关,有“Adapter”就是适配器,有“Observer”就是观察者。第二步看继承和实现关系,确定哪个类实现哪个接口、哪个类继承哪个父类。第三步看方法签名,缺失的方法参数和返回值类型往往能从调用处反推出来。

比如题目给了一个接口Payment,里面有方法pay(double amount),然后有一个类WechatPay实现了它,但方法体是空的,让你填。你只需要在方法体里写一句打印或者返回true就行,因为考试不要求真实逻辑,只要求语法正确、结构完整。

注意:代码填空题里,接口方法前面的public可以省略(接口默认就是public),但实现类里重写方法必须写public,不写会编译错误。这个细节很多人忽略,白白丢分。

6. 常见问题与排查技巧实录

6.1 关系判断总出错怎么办

关系判断出错,九成是因为没有按强弱顺序排除。我的排查流程是这样的:先问“这两个类之间是不是整体和部分的关系”,是的话再问“部分能不能独立存在”,能就是聚合,不能就是组合。不是整体部分关系的话,再问“是不是临时使用关系”,是就是依赖,不是就是关联。最后问“是不是继承或实现”,是的话看箭头和线型。

这个流程走一遍,四种关系基本不会混。我当初把聚合和组合搞混,就是因为跳过了“部分能否独立存在”这一问,看到“包含”就直接画组合。后来强迫自己每道题都走完整流程,正确率才上来。

6.2 设计模式选错怎么补救

设计模式选错,通常是因为只看了关键词没看结构。题目说“支持多种算法”,你选了策略,但题目还说“算法有固定流程、部分步骤可变”,那其实是模板方法。补救方法是:选完模式后,用标准结构反推一遍,看题目给的类能不能填进这个结构。填不进去,说明模式选错了。

比如你选了策略模式,标准结构需要策略接口、具体策略、上下文三个角色。如果题目只给了两个类,没有上下文,那可能不是策略,而是模板方法(父类定义流程、子类实现步骤)。结构反推是检验模式选择的最可靠方法。

6.3 多重度标注的常见误区

多重度标注的误区主要有三个。第一个是只标一端,关联两端都要标,哪怕一端是1也要写出来。第二个是把*和0..*混用,考试里两者等价,但建议统一写0..*,更规范。第三个是忽略题目里的数量限定,题目说“至少一个”就要标1..*,说“可以没有”就标0..*,不能一律标*。

我整理了一张多重度速查表:

题目描述多重度标注
一个A对应一个B两端都标1
一个A对应多个BA端1,B端0..或1..
至少一个BB端1..*
可以没有BB端0..*
多个A对应多个B两端都标0..或

6.4 考前一周的复习节奏建议

考前一周,面向对象这章的复习我建议分三天走。第一天过概念和UML符号,把六种关系的画法和判断口诀默写一遍,把类图元素检查清单过一遍。第二天刷真题,上午题刷近五年面向对象部分,下午题重点做类图和模式填空,做完对答案,错题标注原因。第三天复盘错题,把错题对应的知识点回到教材或者笔记里重新看一遍,确保同样的坑不踩第二次。

我自己的经验是,这章考前突击的性价比很高,因为它的考点集中、题型固定,三天时间足够把高频考点覆盖到位。但前提是你得动手画,光看不动笔,考场上画类图还是会手生。

最后分享一个我做下午题的小习惯:拿到类图题,先在草稿纸上把题目里所有名词列出来,用横线划掉明显是属性的,剩下的就是候选类。然后每个候选类旁边写两三个可能的属性,再根据动词确定方法归属。这套流程走熟了,一道题从读题到画完不超过十五分钟。考试时间紧,这个速度是练出来的,不是看出来的。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进