软件设计师 上午题 设计模式
软件设计师 上午题 设计模式
每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动。[1]
设计模式提供了相关问题的解决方案,使得人们可以更加简单方便地复用成功的设计和体系结构,其中一般有以下4个基本要素:
- 模式名称:一个助记名,用一两个词来描述模式的问题、解决方案和效果。
- 问题:问题描述了应该在何时使用模式。
- 解决方案:解决方案描述了设计的组成成分、它们之间的相互关系以及各自的职责和协作方式。
- 效果:效果描述了模式应用的效果以及使用模式应权衡的问题。
而总的来说,设计模式部分的内容可以按着下面这个表格梳理:

一、创建型设计模式
创建型设计模式的侧重点为对象创建机制,也就是说通过控制对象的创建方式来提升代码的灵活性和可复用性。
因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以及何时创建这些方面给予了很大的灵活性。它们允许用结构和功能差别很大的“产品”对象配置一个系统。配置可以是静态的(即在编译时指定),也可以是动态的(在运行时)。[1]
一般来说,创建型设计模式有以下几种具体的实现方式:
Ⅰ 工厂模式
工厂模式是最常见的设计模式之一,它提供了一种使得创建对象的过程与使用对象的过程分离的方式,使得用户在创建对象时无需指定要创建的具体类。就如同如果朱博宁去买高铁,它只需要去提车即可,不需要关心高铁是如何制造出来的。工厂模式具体有以下几种:
1. 简单工厂模式
简单工厂模式不属于GoF 23种设计模式,但它是工厂方法模式实现的基础。简单工厂模式提供一个静态工厂方法,可以根据传入的参数决定创建哪种具体产品类的实例。但这显然违背了开闭原则(Open for extension, closed for modification),如果要新增产品就要修改工厂逻辑。
2. 工厂方法模式
和简单工厂模式不同的是,工厂方法模式中定义了一个创建对象的接口,这样一来就满足了开闭原则。

在工厂方法模式当中,父类(抽象工厂)只声明创建方法(比如上图中的createProduct()
)而不关心具体实现,子类(具体工厂)负责实现父类的创建方法并返回具体的产品实例。
3. 抽象工厂模式
和工厂方法模式当中创建单一产品不同的是,抽象工厂模式当中创建了一个产品家族,即一系列相关或相互依赖的的对象。

Ⅱ 生成器模式
生成器模式的特点是将对象的构造过程与其表示分离,使得同样的构建过程可以创建不同的表示,主要用于分步骤构建复杂对象,其结构如下所示:

Ⅲ 原型模式
原型模式的核心思想是通过复制现有对象(原型)来创建新对象,而不是通过new
实例化。因此原型模式可以基于克隆快速生成对象,避免重复执行耗时的初始化操作。其结构如下所示:

Ⅳ 单例模式
单例模式确保一个类中只有一个实例,并提供一个全局访问点。它通过控制实例化过程,避免重复创建对象,节省资源。其结构如下所示:

Ⅴ 真题赏析

ACAD,对于(45)而言,所谓的构造一个使用Builder接口的对象
指的就是控制整个流程的类,是Director(指挥者),那么对于本题而言就是Waiter。此外,对于(47)而言,Builder模式适用于:1)创建复杂对象的算法应该独立于该对象的组成部分以及装配方式时;2)当构造过程必须允许被构造对象有不同的表示时。
二、结构型设计模式
结构型设计模式是一类用于处理对象组合和关系的设计模式,其重点解决:1)如何组合类和对象以形成更大的结构;2)如何简化对象间的依赖关系,提升灵活性和可扩展性。
结构型设计模式有以下七种经典模式:
Ⅰ 适配器模式
适配器模式的核心思想是通过一个中间层(适配器)来解决接口不匹配的问题,类似于电源插头转换器的角色。它用于将不兼容的接口转换为客户端期望的接口,使原本无法协同工作的类能够一同工作。适配器模式的结构如下所示:

Ⅱ 桥接模式
桥接模式的核心思想是通过组合代替继承,解决多层继承带来的类爆炸问题,提升系统的灵活性和可扩展性。这使得桥接模式可以将抽象部分和实现部分分离,使得它们可以独立变化。桥接模式的结构如下所示:

其中,抽象是系统的高级控制逻辑,定义客户端直接使用的接口和行为。抽象并不关心具体实现细节,而是通过组合持有一个实现的引用。而实现则是系统的底层核心操作,独立于抽象,可以自由变化或者替换。
Ⅲ 组合模式
假如现在有这么一个公司组织结构:
1 |
|
在组合模式当中,无论是部门(容器节点)还是员工(叶子结点),都可以被统一看作“公司成员”。它们之间的区别是叶子结点直接实现基础行为,而容器节点除了自身行为之外还可以递归调用子节点的方法。例如在计算全公司人数时,只需要让每个“公司成员”返回自己的人数并进行递归累加即可,无需区分部门还是员工。
这就是组合模式,它会将对象组合成树形结构以表示“部分-整体”的层次关系,使得用户对单个对象和组合对象的使用具有一致性。其结构如下图所示:

Ⅳ 装饰器
装饰器的核心思想是通过嵌套包装对象来扩展功能,而非通过子类继承。因此,装饰器模式可以用于动态地为对象添加额外职责。其结构如下所示:

Ⅴ 外观模式
外观模式的核心思想是用一个高层接口包装一组接口,让客户端只需调用一个简单方法就能完成复杂功能。这样一来就可以为复杂的子系统提供一个统一的简化接口,隐藏内部细节,降低客户端与子系统的耦合度。外观模式的结构如下图所示:

其中,Facade知道哪些子系统类复制处理请求,会将客户的请求代理给适当的子系统对象。
Ⅵ 享元模式
享元模式的核心思想是分离对象的固有状态(共享)和外部状态(非共享),从而通过共享技术高效地支持大量细粒度对象的复用,进而减少内存占用和对象创建开销。享元模式的结构如下图所示:

例如在一款射击游戏当中需要渲染大量子弹,对于每颗子弹而言有以下属性:
- 固有状态(共享):纹理图片、颜色、伤害值等(所有子弹相同)。
- 外部状态(非共享):位置坐标、飞行角度等(每颗子弹不同)。
享元模式就是共享相同纹理的子弹对象来解决内存消耗过大的问题。
Ⅶ 代理模式
代理模式通过创建一个代理对象来控制对原始对象的访问,在不改变原始类的情况下增强功能。代理模式的核心思想是由“代理”充当中间人,在客户端和目标对象之间插入一层,用于添加额外逻辑。其结构如下图所示:

Ⅷ 真题赏析

CDAB,桥接模式适用于:
- 不希望在抽象和它的实现部分之间有一个固定的绑定关系。
- 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。
- 对一个抽象的实现部分的修改应对客户不产生影响,即客户代码不必重新编译。
- (C++)想对客户完全隐藏抽象的实现部分。
- 有许多类要生成的类层次结构。
- 想在多个对象间共享实现(可能使用引用计数),但同时要求客户并不知道这一点。

CD,对于(45)题的A选项而言,这是责任链模式的适用情况。
三、行为型设计模式
行为型设计模式是设计模式当中的最后一大类,和先前介绍的注重于对象创建机制的创建型设计模式、注重于对象组合和关系的结构型设计模式不同的是,行为型设计模式专注于对象之间的交互方式和算法的封装。这类设计模式不关注对象如何创建或者组合,而是定义对象如何协作完成任务。
常见的行为型设计模式有以下11种(死…):
Ⅰ 责任链模式
责任链使得多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。它允许用户将请求沿着处理链传递,直到有一个处理者能够处理它。其结构如下图所示:

Ⅱ 命令模式
命令模式将请求封装为独立的对象(命令),允许用户参数化客户端(调用者)与接收者(执行者),从而支持请求的排队、撤销、日志记录等操作。其结构如下图所示:

Ⅲ 解释器模式
解释器模式的核心思想是“将语言种的每个语法规则表示为一个类,通过组合这些类来构建语法树,最终解释执行”。因此这种设计模式可以用于定义一种语言的语法规则,并提供一个解释器来解释该语言中的表达式。其结构如下图所示:

Ⅳ 迭代器模式
迭代器模式提供一种方法顺序访问聚合对象(列表、树等)中的元素,而不需要暴露该对象的内部表示,这实际上是将遍历逻辑从聚合对象中分离出来了。这种设计模式的结构如下所示:

Ⅴ 中介者模式
中介者模式的核心思想是“使用一个中介者来封装一系列对象之间的交互,使各个对象不需要显式地相互引用”,即通过引入中介对象来集中管理多个对象之间的交互,从而减少对象间的直接耦合,使系统更易于维护和扩展。这种设计模式的结构如下图所示:

Ⅵ 备忘录模式
备忘录模式的核心思想是“在不暴露对象实现细节的情况下,保存和恢复其内部状态”,它允许在不破坏封装性的前提下捕获并外部化对象的内部状态,以便后续可以恢复到该状态。其结构如下图所示:

Ⅶ 观察者模式
在观察者模式当中定义了对象间的一对多依赖关系,当一个对象(主题)状态改变时,所有依赖它的对象(观察者)都会自动收到通知并更新。其结构如下图所示:

Ⅷ 状态模式
状态模式的核心为:同一个动作在不同状态下有不同的表现。例如日常用的台灯,正常状态下按开关出现的是白光,护眼模式下同样按开关出现的却是黄光。
(状态模式)允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。[1]
状态模式的结构如下图所示:

Ⅸ 策略模式
策略模式是一种将算法抽离出来的设计模式,它定义一系列算法(策略),并将每个算法封装成独立类,使它们可以互相替换,且算法的变化不影响使用它的客户端。该模式的结构如下图所示:

Ⅹ 模板方法
模板方法模式的核心思想为:“在不改变算法结构的情况下,允许子类重写算法的特定步骤”,这就类似于我们在按照食谱做一道菜的时候,大致的步骤是固定的,但其中诸如撒盐多少的细节就可以根据个人喜好进行调整。也就是说,模板方法模式在父类当中定义算法的骨架,而将某些步骤的具体实现延迟到子类当中完成,其结构如下图所示:

XI 访问者模式
最后一个!!!
访问者模式适用于对象结构稳定但需要频繁新增操作的场景,它可以将数据结构和数据操作分离,允许在不修改对象类的前提下,让新增操作像“访问者”一样灵活接入。这种设计模式的结构如下图所示:

三、真题赏析
- 软件设计师教程(第五版). (2018). ↩