软考中级软件设计师知识地图:面向对象、设计模式与算法速查

发布时间:2026/10/28 2:11:34
软考中级软件设计师知识地图:面向对象、设计模式与算法速查

简介:面向软件设计师中级考试的系统化知识点整理文档,适合备考软考的考生及需要梳理计算机基础理论的学习者使用,聚焦面向对象设计、UML建模、树与二叉树、线性表及排序算法等高频考点。包内仅含1个docx文件,压缩包约31.83MB,文档以目录化笔记形式组织,涵盖面向对象基本概念与七大设计原则、创建型/结构型/行为型模式分类、UML关系辨析,以及树的概念、查找二叉树、平衡二叉树、特殊树、各种排序算法对比与情景选择等内容。已有894人学习下载,便于读者按目录快速定位薄弱章节,对照概念与特性做重点记忆,也可作为考前串联知识框架、查漏补缺的复习材料。

1. 软考中级到底在考什么:一张知识地图先看懂

很多人第一次翻开软件设计师中级考试的目录,会有种打开别人家抽屉的感觉:里面什么都有,摆得也整齐,但就是不知道该先拿哪一样。软考中级这份知识点整理,做的事情其实就是把这个抽屉按用途重新归了一遍类——面向对象、UML、数据结构、算法、软件工程、网络、数据库,十几个大格子,每个格子里再分小格。打个比方,它更像一张城市地图而不是一本词典:词典告诉你某个词怎么解释,地图告诉你这些地方彼此怎么走通。搞懂这张地图的意义在于,你不必背下所有细节,但得知道「设计模式」和「面向对象原则」住在同一条街上,「关键路径」和「进度管理」说的其实是同一件事的两种口径。方向对了,后面每一章才不至于变成玄学。

2. 面向对象那些名词:类、封装、多态到底在解决什么问题

2.1 从「一堆函数」到「一群对象」:封装封的是变化的边界

先想象一个没有面向对象的订单系统。所有逻辑都写成函数,共享一批全局变量:订单号、金额、状态、收货地址。刚开始很顺手,等到某天要加一个「部分退款」功能,你会发现改一个字段,另外七八个函数的行为全跟着变,谁调用了谁全靠记忆。这类代码的痛感不是「难写」,而是「不敢改」。

面向对象的第一刀,就是把这些散落的数据和操作它们的方法捆成一个整体。类是这个整体的模板,规定了有哪些属性、能做哪些动作;对象是按这个模板造出来的具体实例。更通俗地说,类是月饼模子,对象是磕出来的月饼,模子只有一个,月饼可以有无数个,每个的馅还能不一样。封装指的是:对象的状态不直接暴露给外面,外面只能通过它提供的方法来读写。好处是修内部实现时,只要方法签名不变,外面的调用方完全不用动。

继承表达的是「是一种」的关系。子类拿到父类的属性和方法,再补上自己特有的部分。多态则是同一个调用在不同对象身上给出不同结果:同样是「计算运费」,普通用户、会员、大客户走的是三套算法,但调用方只写一次order.calculateShipping()。抽象是把共性抽出来、把差异留给子类;组合是「有一个」,把一个对象放进另一个对象里当零件用。这五个词之所以总被一起讲,是因为它们本来就是同一件事的五个侧面——让变化被关在尽量小的盒子里。

提示:封装不等于「把字段写成 private 就完事」。判断有没有真正做到封装,看的是外部能不能绕过方法直接改动内部状态,而不只是看修饰符。

2.2 七大设计原则:每一条背后都对应一种「改不动」的痛苦

这些原则不是凭空立的规矩,每一条都对应一类真实项目里反复出现的翻车现场。把它们当成七种「病」的「对症药」来记,比死背定义牢固得多。

单一职责原则说的是一个类只有一个引起它变化的原因。反例很好找:一个类既负责算钱,又负责发通知,还负责写数据库。结果是财务规则一变要动它,通知渠道一变还要动它,谁都不敢碰。开闭原则说的是对扩展开放、对修改关闭。最典型的违规是那条越写越长的 if-else 支付分支,每接一家新渠道就要往里加一个 else,改到最后没人敢动这段代码。里氏替换原则说的是任何出现父类的地方,换成子类都应该不出问题。教科书级的反面例子是让「正方形」继承「长方形」:长方形的 setWidth 和 setHeight 是独立的,正方形里只要改宽,高就得跟着改,替换之后行为就坏了。

依赖倒置原则说的是高层模块和低层模块都依赖抽象,而不是高层直接依赖具体实现。业务层里直接 new 一个具体的数据库访问类,就是最典型的违规,换库的时候只能全篇替换。接口隔离原则说的是别逼一个类去实现它根本用不上的方法。那种十几个方法的大接口,实现类里塞满空实现和抛异常的占位代码,就是接口太肥的信号。迪米特法则叫「最少知识原则」,一个对象应该尽量少地跟陌生对象打交道,a.getB().getC().doSomething()这种链式穿透就是典型违规,中间任何一环改了都会波及。合成复用原则说的是优先用组合而不是继承来复用。为了拿到父类的两个工具方法就继承一整个类,属于用大炮打蚊子,代价是永久绑死了继承链。

原则一句话概括典型违规现场
单一职责一个类只为一个变化负责一个类同时管账、管通知、管存储
开闭加功能靠新增,不靠改老代码每接一个渠道就加一个 if-else 分支
里氏替换子类能无缝替换父类正方形继承长方形后行为错乱
依赖倒置双方都依赖抽象业务层直接 new 具体实现类
接口隔离接口要小而专大接口下挂一堆空实现
迪米特少跟陌生人说话层层 get 穿透调用
合成复用优先组合,慎用继承为复用两个方法继承整个基类

注意:这七条在实践中会互相拉扯。把类拆到极致,类的数量会爆炸;接口切得太碎,调用方要拼装一堆接口。原则是用来权衡的坐标,不是用来打卡的清单。

2.3 为什么这一块总在考:题目里它们长什么样

考试对这块的考法,很少直接问「开闭原则的定义是什么」,更多是给一段场景,让你判断它违反了哪一条,或者给出一个设计问你怎么改才更符合原则。这类题的通用解法是两步:先找出场景里的「变化点」在哪,再对照七条原则里哪一条正对着这个变化点。变化点在「新增功能要改老代码」上,就往开闭上想;变化点在「高层直接依赖了具体类」上,就往依赖倒置上想;变化点在「实现类被迫写了一堆空方法」上,答案基本就是接口隔离。

另一类常见问法是让你区分原则和模式。原则是方向,模式是这套方向下被总结出来的具体套路。比如「策略模式」之所以存在,就是开闭原则和依赖倒置在「算法可替换」这个场景里的落地;「装饰器模式」对应的是开闭原则下「不改原类但要加功能」的需求。把原则当成原因、模式当成结果去看,两边的知识就能串成一条线,而不是两块互不相干的背诵材料。

3. 设计模式与 UML:为什么这两块总被绑在一起考

3.1 三种模式分类的依据:创建型、结构型、行为型怎么一眼分开

二十多种模式如果硬背,必然记混。真正好用的分法只有一句话:创建型管「对象怎么生」,结构型管「对象怎么拼」,行为型管「对象怎么动」。

创建型模式关心的是把对象的创建过程从使用方手里拿开,让调用方不必关心具体造的是哪个类。工厂方法把「造什么」推迟到子类,抽象工厂处理的是「一整族相关产品」的成套创建,建造者把「一步步组装复杂对象」和「最终产物」分开,原型靠克隆已有对象来造新的,单例保证全局只有一个实例。它们的共同点是:都在解决 new 这件事带来的耦合。打个比方,创建型就像零件车间,你只说要什么规格,不用管车床怎么转。

结构型模式关心的是把已有的类和对象组合出更大的结构。适配器解决接口对不上,桥接把抽象和实现拆成两条独立的继承线,组合把「整体—部分」做成树形结构,装饰器在不改原类的前提下动态加职责,外观给一堆子系统配一个统一入口,享元把大量细粒度对象里可共享的部分抽出来,代理在前面挡一层控制访问。它们像装配车间,零件本身不变,变的是拼装方式。

行为型模式关心的是对象之间的职责分配和通信。策略把可替换的算法独立成一族类,状态让对象在内部状态改变时改变行为,观察者建立一对多的通知关系,责任链把请求沿链传递直到有人处理,命令把请求封装成对象以便排队和撤销,模板方法把算法骨架固定、把可变步骤留给子类。它们是机器运转时的协作规则。

分类解决的问题典型成员一句话记忆
创建型对象创建与使用解耦工厂方法、抽象工厂、建造者、原型、单例只管生,不管用
结构型类与对象的组合方式适配器、桥接、组合、装饰器、外观、享元、代理只管拼,不管动
行为型职责分配与对象通信策略、状态、观察者、责任链、命令、模板方法只管动,不管生

3.2 UML 的图与关系:类图上那条线为什么有那么多种画法

UML 是描述软件系统结构和行为的统一建模语言。图分两大类:结构图描述「系统长什么样」,包括类图、对象图、组件图、部署图、包图等;行为图描述「系统怎么动」,包括用例图、活动图、状态机图、时序图、通信图等。考试里出现频率最高的是类图、用例图和时序图。

类图里那些线,考的是关系强弱。从弱到强大致是:依赖、关联、聚合、组合,另外还有两条不算在强弱序列里的——泛化和实现。依赖是「用一下就走」,一个类的方法参数或局部变量里出现了另一个类;关联是长期的结构性联系,通常体现为成员变量;聚合是「有,但可以分开」,比如部门和员工,部门没了员工还在;组合是「有,且同生共死」,比如订单和订单明细,订单删了明细也不该单独存在。泛化就是继承,表达「是一种」;实现是类对接口的兑现,表达「能做到」。

这个强弱顺序之所以重要,是因为它直接影响代码里的耦合程度。依赖最松,改起来最容易;组合最紧,但语义最强。判断题里常给出一个场景问「这两个类之间是什么关系」,判断依据其实就一条:生命周期是否绑定。绑定的往组合上靠,不绑定的往聚合上靠。

关系语义代码里的常见形态生命周期
依赖临时使用方法参数、局部变量不绑定
关联长期联系成员变量各自独立
聚合整体与部分成员变量,可从外部传入部分可独立存在
组合强整体与部分成员变量,内部创建同生共死
泛化是一种extends强耦合
实现能做到implements面向接口

3.3 把模式和图对上:一道题里两边怎么互相验证

模式和 UML 之所以总绑在一起,是因为它们本来就是同一件事的两种表达:模式是文字描述的设计经验,类图是这种经验的图形化。看到一对多的通知结构,中间那个抽象主题持有一组观察者接口,几乎可以直接判断是观察者模式;看到一个上下文类里持有一个算法接口的引用,并且这个接口可以由外部替换进去,基本就是策略模式;看到一个类的方法里调用另一个类的模板方法、自己只填几个钩子,那是模板方法。

反过来的题也存在:给出一个模式的名称,问它在类图上应该体现为什么关系。这时候应该抓住「谁持有谁、谁继承谁」这两个问题。策略模式是上下文持有策略接口,属于关联或聚合;装饰器是装饰器继承抽象构件并持有一个抽象构件,属于既泛化又聚合;适配器是适配器实现目标接口并持有一个被适配者,属于实现加关联。把这几组固定搭配记住,图和模式之间的翻译就不再靠猜。

提示:做这类题时先别急着数类名,先看箭头的方向。箭头指向接口通常是实现,指向父类通常是泛化,菱形出现在整体那一端表示聚合或组合。

4. 数据结构与算法:树、排序、分治贪心动态规划的边界在哪

4.1 树与二叉树:为什么非要区分「查找二叉树」和「平衡二叉树」

树是一种非线性结构,只有根节点没有父节点,每个节点可以有零个或多个子节点,没有子节点的叫叶子。几个必背的量包括节点的度(子节点个数)、树的深度(最大层数)和层次(从根开始数)。二叉树是每个节点最多两个子节点的特例,它的几条性质在题里出现得极频繁:第 i 层最多 2^(i-1) 个节点,深度为 k 的二叉树最多 2^k − 1 个节点,叶子节点数总比度为 2 的节点数多一个。完全二叉树还有一套编号规律,父子节点的下标关系可以直接算出来,堆排序就是靠这个规律落地的。

查找二叉树(也叫二叉排序树)加了一条约束:左子树所有节点小于根,右子树所有节点大于根。它带来的好处是中序遍历出来天然有序,查找时能一路比较着往下走。但它有个致命短板:如果按从小到大依次插入,整棵树会退化成一条链,查找从对数级掉回线性级。平衡二叉树(AVL 树)就是为了堵这个窟窿——它要求任意节点左右子树高度差不超过 1,一旦插入或删除破坏了这个条件,就通过 LL、RR、LR、RL 四种旋转把树掰回来。所以两者的关系不是并列的两种树,而是「理想很丰满」和「防止它退化」的关系。红黑树是平衡策略的另一种折中,它不追求严格平衡,用颜色约束换取更少的旋转次数;堆是满足父节点与子节点大小关系的完全二叉树;哈夫曼树则是带权路径长度最短的树,压缩编码里用得多。

4.2 线性表与串:顺序存储和链式存储到底在换什么

顺序存储是拿一段连续空间依次摆开,好处是按下标访问是常数时间,坏处是插入删除要挪一串元素,而且容量得提前估。链式存储是把元素散在各处,用指针串起来,好处是插入删除只改几个指针,坏处是不能随机访问,要找第 k 个必须从头数,另外每个节点还要额外空间存指针。这两者的取舍本质上是在「读多」还是「写多」之间做选择,没有绝对优劣。考试里常考的还有存储密度这个指标,它衡量的是数据本身占的空间和整个节点占的空间之比,链式存储因为要存指针,密度必然小于 1。

串是特殊的线性表,元素只能是字符。串的模式匹配里,朴素算法在主串和子串失配时要把主串指针回退,最坏情况下复杂度到 O(m×n)。KMP 的思路是让主串指针永不回退,靠一个 next 数组记录「失配时子串该跳到哪一位」,把复杂度压到 O(m+n)。这个 next 数组的含义常被误解成「记录前缀和后缀的最长公共长度」,其实它更准确的作用是告诉匹配过程:已经匹配上的这一段里,有多大一截可以继续复用。

4.3 四类算法思想:分治、回溯、贪心、动态规划的分水岭

这四种思想在题目里经常混着出现,但它们的边界其实很清楚,判断的关键有两个问题:子问题能不能独立求解,以及能不能反悔。

分治是「把大问题切成互不相交的小问题,解完再合并」。它成立的前提是子问题独立、规模缩小后结构不变。二分查找、归并排序、快速排序都是这个路子。回溯是「试一步,不行就退回来换一条」。它在一棵隐式的解空间树上做深度优先搜索,走不通就撤销选择。八皇后、迷宫路径都属于这一类,特征是解需要满足一堆约束,且必须整体满足。贪心是「每一步都拿当前看起来最好的,拿了就不回头」。它能成立需要两个条件:贪心选择性质和最优子结构。部分背包问题可以用贪心,因为物品可以切分;0-1 背包就不能,因为切不动。动态规划是「把子问题的解记下来,避免重复计算」。它适用于有重叠子问题和最优子结构的问题,本质是用空间换时间。

一句话把它们分开:分治的子问题互不重叠,动态规划的子问题会重复;贪心不回头,回溯必须能回头。下面这段二分查找是分治思想最朴素的落地,边界处理是它最容易踩坑的地方。

def binary_search(nums, target): # nums 必须是有序数组,这是二分成立的前提 left, right = 0, len(nums) - 1 while left <= right: # 用 <= 保证区间只剩一个元素时仍会检查 mid = left + (right - left) // 2 # 这样写避免 left+right 溢出 if nums[mid] == target: return mid # 命中,直接返回下标 elif nums[mid] < target: left = mid + 1 # 目标在右半区,左边界右移 else: right = mid - 1 # 目标在左半区,右边界左移 return -1 # 循环结束仍未命中

这段代码里,nums是待查的有序数组,target是要找的值,返回值是下标或 −1。关键在于两个边界:循环条件用left <= right而不是<,以及更新时用mid ± 1而不是mid。前者的区别是「区间内只剩一个元素时要不要再比一次」,后者的区别是「会不会死循环」。这两个地方任意一处写错,程序在特定输入下就会翻车,而且往往只在边界数据上暴露。

递归和动态规划的差别也可以用斐波那契说明:直接递归会把同一个子问题算无数遍,复杂度指数级;加一个记忆化的表,复杂度立刻降到线性。这个「加表」的动作,就是动态规划最核心的一步。

5. 易混概念辨析:这几组知识点看着像,其实完全不同

5.1 阶段式与连续式:CMMI 的两个模型不是新旧关系

易混点在于,两个模型都带「等级」这个词,很容易被当成同一套东西的两种叫法。之所以会混,是因为它们都用来衡量软件过程改进的水平,而且都分等级,光看名字根本分不出来。

区分的关键是「衡量对象」不同。阶段式模型衡量的是整个组织的成熟度,它把组织作为一个整体来评级,回答的是「这家组织整体走到了哪一步」。连续式模型衡量的是单个过程域的能力,它不给你一个组织的总等级,而是逐个过程域打分,回答的是「这个具体环节做到了什么程度」。打个比方,阶段式像学校的综合排名,连续式像每门课的单科成绩。一个组织可能总体排在某个级别,但某个过程域的单科水平明显更高,这两件事并不矛盾。

5.2 主动攻击与被动攻击:一刀切在「数据有没有被改动」

易混点在于,两种攻击都发生在网络通信过程中,目的都是获取信息,光看「攻击」两个字分不出来。之所以会混,是因为现实中一次完整的攻击往往两种手段都用。

区分的标准只有一条:攻击过程中,数据本身有没有被改动。被动攻击是「只看不动」,包括窃听通信内容、分析通信双方的流量特征,它不修改数据,也不影响系统正常运行,所以极难被发现,防御思路主要靠加密而不是靠检测。主动攻击是「动手改了」,包括篡改数据、重放旧消息、伪造身份、中断服务,它会改变数据或系统状态,因此可以被检测出来,防御思路侧重于完整性校验和身份认证。做题时只要问一句「数据被动过吗」,答案就出来了。

5.3 关键路径与 Gantt 图:一个算时间,一个看排布

易混点在于,两者都是进度管理里的工具,都用来表示任务和工期,在题目里经常同时出现。之所以会混,是因为它们描述的是同一批任务的同一种属性——时间。

关键路径法算的是一张网络图里从起点到终点的最长路径,这条路径的长度就是项目的最短总工期,路径上的活动没有半点浮动时间,任何一个延迟都会直接推迟整个项目。Gantt 图则是把任务画成横条排布在时间轴上,直观展示每个任务什么时候开始、什么时候结束、哪几段在并行。前者擅长表达依赖关系和找出瓶颈,后者擅长表达时间占用和并行情况,但它对任务之间的依赖关系表达得比较弱。一句话:关键路径回答「哪几个活动拖不得」,Gantt 图回答「这段时间大家在忙什么」。

5.4 对称加密与数字签名:一个保机密,一个保身份

易混点在于,两者都用到了加密算法,名字里都带「密」字,很容易被当成同一类技术。之所以会混,是因为数字签名确实建立在非对称加密的数学基础上。

区分要看它们保护的东西不同。对称加密用同一把密钥加密和解密,解决的是「内容别被别人看见」,特点是快,适合大批量数据,难点是密钥怎么安全地送到对方手里。数字签名用私钥对摘要签名、用公钥验证,解决的是「这条消息确实是你发的、而且你赖不掉」,它不负责保密,只负责身份和完整性。举个反例就清楚了:一份文件用对称加密发出去,能保证别人看不到,但没法证明是谁发的;加了数字签名,能证明是谁发的,但如果不用额外手段,文件内容本身还是明文。两者经常搭配使用,各管一头。

注意:数字签名不提供保密性,这一点在各种判断题里被反复设置陷阱。看到「签名后内容不可见」这种表述,基本可以直接判错。

6. 什么时候该用这份整理、什么时候别死磕它

这份按目录组织的知识点整理,最合适的用法是「先定位、再精读」。拿到它之后别从第一页开始顺读,先花二十分钟把目录过一遍,在纸上画出自己的薄弱区域——如果你对「里氏替换」只有一个模糊印象,那第 2 章那一段就该重点看;如果你分不清主动攻击和被动攻击,就直接跳到辨析那一节。它的价值在于结构清晰、条目对齐考点,适合当查询手册和查漏补缺的清单,不适合当唯一的入门教材。

具体来说,三种场景下它特别顺手。第一种是复习到某个名词突然卡壳,翻目录定位到对应小节,三五分钟就能把定义、特征和易混点一次看全。第二种是考前做错题回顾,把错题按章节归类,很快就能看出自己是集中在某几块失分,还是全盘都不牢。第三种是面试或工作中临时要用到某个概念,比如突然需要说清聚合和组合的差别,翻到关系对比那一节,表格看一眼就够。

反过来,有三种情况别指望它。一是完全没有编程和项目经验时,直接啃设计模式那几章会很痛苦,因为模式本身就是从大量真实代码里总结出来的,没有代码量打底,那些「为什么这么设计」的动机是感受不到的。二是别把它当成唯一的题源,知识点整理只回答「是什么」,不回答「题目怎么绕」,还需要配合真题去体会出题角度。三是别陷入抄写式背诵,七大原则、二十几种模式、各种算法复杂度,抄十遍不如自己用一个具体场景复述一遍。

场景建议用法不建议的做法
初次接触某个概念定位到小节,读定义加类比从第一章顺读到最后一章
错题复盘按章节归类错题,找集中失分区只记答案不记原因
考前冲刺只看对比表和易混辨析重新逐字背诵全部内容
零项目经验先补代码实践再看模式章节硬背模式名称和类图

从那以后我每次拿到一份知识点整理,都会强制走一遍「先看目录画地图、再挑三章精读、最后用一张表把易混点收口」的流程,宁可少看两章,也不让知识停在似懂非懂的状态。希望帮到你。

本文还有配套的精品资源,点击获取