跳转至

设计模式分类、类图与考题解析

根据用户提供的软件设计师设计模式讲义整理,覆盖 GoF 的 23 种模式及其分类、意图、类图角色和识别方法。31 张原配图已保存到本地,三道综合例题保留题干和选项并统一编号。

正文中的解释、辨析与参考答案为整理补充,不代表官方解析。原图保留讲义样貌,其中的英文笔误、简化说法和角色标注以正文说明为准。需要具体代码时,可继续阅读各模式详解与示例代码。

考点一:模式分类与识别方法

常见考法有四类:根据应用场景识别模式、根据类图识别模式、判断模式分类、分析图中各个类承担的角色。

两个分类维度

**创建型、结构型、行为型**回答“模式主要解决什么问题”;**类模式、对象模式**回答“主要通过什么关系组织协作”。两组分类可以组合,例如“创建型对象模式”。

按目的分类 关注的问题 数量
创建型 对象如何创建,怎样隔离具体类型和构建过程 5
结构型 类与对象如何组合,怎样适配接口、扩展结构 7
行为型 对象如何分配职责、执行算法、处理请求和交互 11

类模式主要利用继承,由子类改变或补充行为;对象模式主要利用对象组合与委托。不能因为图中画了类,或者出现了继承箭头,就把模式判为类模式:对象模式也经常使用继承和接口实现。

原讲义按目的与作用范围划分的设计模式分类表

模式 英文名称 按目的分类 按作用范围分类
抽象工厂 Abstract Factory 创建型 对象
工厂方法 Factory Method 创建型 类
原型 Prototype 创建型 对象
单例/单件 Singleton 创建型 对象
建造者/生成器/构建器 Builder 创建型 对象
适配器 Adapter 结构型 类或对象,取决于实现形式
桥接 Bridge 结构型 对象
组合 Composite 结构型 对象
装饰/装饰者 Decorator 结构型 对象
外观/门面 Facade 结构型 对象
享元 Flyweight 结构型 对象
代理 Proxy 结构型 对象
观察者 Observer 行为型 对象
访问者 Visitor 行为型 对象
状态 State 行为型 对象
职责链/责任链 Chain of Responsibility 行为型 对象
命令 Command 行为型 对象
解释器 Interpreter 行为型 类
迭代器 Iterator 行为型 对象
中介者 Mediator 行为型 对象
备忘录 Memento 行为型 对象
策略 Strategy 行为型 对象
模板方法 Template Method 行为型 类

原文误把迭代器写成 Interpreter、模板方法写成 Strategy,正确名称分别是 Iterator 和 Template Method。

分类口诀怎么用

  • 原讲义“公司模姐”:按“工、适、模、解”记忆工厂方法、类适配器、模板方法、解释器四种类模式形式。适配器同时还有对象模式形式,其他模式归入对象模式。
  • 原讲义“四桥组装外箱带”:按“适、桥、组、装、外、享、代”记忆七种结构型模式:适配器、桥接、组合、装饰、外观、享元、代理。
  • 创建型记住五种:抽象工厂、工厂方法、原型、单例、建造者;剩余十一种是行为型。

这些口诀只用于记分类。简单工厂不在 GoF 的 23 种模式之内,不能把它和工厂方法混算。

做题顺序:先看意图,再找结构证据

  1. 判断变化点:题目是在更换创建方式、组合结构,还是算法与交互行为。
  2. 用关键词缩小范围,例如“产品族”“分步构建”“整体与部分”“一对多通知”。
  3. 对照类图:谁继承谁、谁持有谁、谁调用谁,关键方法定义在哪个角色上。
  4. 再回答模式分类和具体角色,避免仅凭类名或单个词下结论。

“可撤销”可能涉及命令或备忘录,“两组继承结构”也不必然是桥接。关键词提供线索,意图与协作关系决定答案。

考点二:创建型模式

抽象工厂(Abstract Factory)

意图:提供创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。

抽象工厂创建 A、B 两类产品及不同产品族的类图

角色 职责
AbstractFactory 声明不同产品种类的创建方法,如 createProductA()、createProductB()
ConcreteFactory1、ConcreteFactory2 分别创建某一产品族中的配套产品
AbstractProductA、AbstractProductB 定义不同产品种类的抽象接口
ProductA1、ProductB1 等 具体产品;A1 与 B1 属于同一产品族
Client 依赖抽象工厂和抽象产品,使用选定的一族产品

例如 GUIFactory 定义 createButton()、createCheckbox(),WindowsFactory 创建 Windows 风格的按钮和复选框,MacFactory 创建另一套配套控件。

关键词:产品族、成套产品、一系列相关对象、联合使用。重点是创建一族配套产品,不能仅凭“有多个 createXXX() 方法”判定模式。增加产品族通常只需增加具体工厂和产品;增加产品种类则往往需要修改工厂接口及各具体工厂。

详见:抽象工厂模式。

工厂方法(Factory Method)

意图:定义创建对象的接口,让子类决定实例化哪个具体类,把实例化的决定延迟到子类。

工厂方法的产品层次与创建者层次

角色 职责
Product 定义产品接口
ConcreteProduct 实现产品接口,提供具体产品行为
Creator 声明返回 Product 的工厂方法,也可以包含使用产品的通用业务流程
ConcreteCreator 重写工厂方法,决定返回哪个具体产品

例如 Dialog 声明 createButton(),WindowsDialog 返回 WindowsButton,WebDialog 返回 HtmlButton;对按钮的通用使用流程可以留在 Dialog 中。

关键词:子类决定创建哪种对象、重写创建方法。“延迟到子类”描述职责分配,不等于延迟加载,也不是指让对象晚一点创建。原文把它简称为“工厂模式”,本篇统一使用“工厂方法”以免与简单工厂、抽象工厂混淆。

详见:简单工厂与工厂方法。

原型(Prototype)

意图:用原型实例指定创建对象的种类,通过复制该原型创建新的对象。

通过克隆原型实例创建新对象的类图

角色 职责
Prototype 声明复制自身的操作
ConcretePrototype 实现具体复制逻辑
Client 选择原型并请求复制

关键词:克隆、拷贝已有对象、原型实例。复制后哪些数据独立、哪些引用共享,要区分浅拷贝与深拷贝。Java 的 Cloneable 是标记接口,本身不声明 clone();原型模式也可以通过复制构造器等方式实现。

详见:原型模式。

单例(Singleton)

意图:保证一个类只有一个实例,并提供访问该实例的全局访问点。

单例类保存唯一实例并通过静态访问方法返回它

核心角色是 Singleton:控制实例创建,保存并返回唯一实例。getInstance() 或图中的 Instance() 是常见访问方法名。

关键词:唯一实例、统一访问点。唯一性有作用范围,例如 Java 中通常针对同一类加载器加载的类;它不会自动保证跨进程唯一,也不会自动保证实例中的业务方法线程安全。

详见:单例模式。

建造者/生成器(Builder)

意图:将一个复杂对象的构建与它的表示分离,使同样的构建过程可以创建不同的表示。

Director 编排步骤,ConcreteBuilder 构建 Product 的类图

角色 职责
Builder 声明构建产品各部分的步骤接口
ConcreteBuilder 实现各步骤,构建并装配产品,提供取得结果的方法
Director 通过 Builder 接口安排构建步骤及其顺序
Product 最终构建出的复杂对象

“表示”可以理解为最终对象由哪些部件组成、各部件如何配置。相同的“制作主餐 → 配饮料 → 配玩具”流程,可以由不同建造者完成,得到不同套餐。

关键词:复杂对象、分步构建、同一流程得到不同表示。Product 是构建结果,不能把它和负责构建的 ConcreteBuilder 混为一谈;现代实现可以由客户端直接编排步骤,但本讲义题目采用含 Director 的经典结构。

详见:建造者模式。

创建型模式对照

原讲义创建型模式的意图与关键词汇总

题干最有区分度的信息 优先考虑
需要一套配套产品,例如按钮和复选框都使用同一种风格 抽象工厂
由子类决定创建哪种具体产品 工厂方法
按同样步骤构建复杂对象,但部件或结果表示不同 建造者
复制一个已有实例得到新对象 原型
控制唯一实例并提供统一访问点 单例

原图将工厂方法简记为“动态生产对象”,区分度不够:多种创建型模式都能在运行时创建对象,判断工厂方法时应找“由子类决定”。

例题 1:儿童套餐的制作

某快餐厅主要制作并出售儿童套餐,一般包括主餐(各类比萨)、饮料和玩具,其餐品种类可能不同,但制作过程相同。前台服务员(Waiter)调度厨师制作套餐。欲开发一软件,实现该制作过程,设计如下所示类图。

该设计采用(①)模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。其中,(②)构造一个使用 Builder 接口的对象。该模式属于(③)模式,该模式适用于(④)的情况。

Waiter 调度 PizzaBuilder 构建不同 Pizza 的例题类图

选项 第①空:模式 第②空:角色 第③空:分类
A 生成器(Builder) PizzaBuilder 创建型对象
B 抽象工厂(Abstract Factory) SpicyPizzaBuilder 结构型对象
C 原型(Prototype) Waiter 行为型对象
D 工厂方法(Factory Method) Pizza 结构型类

第④空选项:

  • A、当一个系统应该独立于它的产品创建、构成和表示时
  • B、当一个类希望由它的子类来指定它所创建的对象的时候
  • C、当要强调一系列相关的产品对象的设计以便进行联合使用时
  • D、当构造过程必须允许被构造的对象有不同的表示时
参考答案与解析

第①空选 A。“复杂对象的构建与表示分离”“同样的构建过程”直接对应建造者。

第②空选 C。题干“构造一个使用 Builder 接口的对象”是在描述 Director 的职责:通过 Builder 接口组织构建过程,并非询问谁实现 Builder 接口。

题图中的类 模式角色
Waiter Director,安排制作流程
PizzaBuilder Builder,声明制作步骤
HawaiianPizzaBuilder、SpicyPizzaBuilder ConcreteBuilder,执行具体制作步骤
Pizza Product,构建结果

第③空选 A。Builder 是创建型对象模式。

第④空选 D。它最明确地对应“同一构建过程允许不同表示”。B 强调子类决定实例化类型,对应工厂方法;C 强调成套产品联合使用,对应抽象工厂。A 是较宽泛的创建解耦描述,原型等模式也有类似适用条件,不能仅凭它定位建造者。

套餐里有多种部件,并不意味着一定是抽象工厂;这道题强调的是分步组装一个复杂产品。

考点三:结构型模式

适配器(Adapter)

意图:把一个类的接口转换成客户希望的另一个接口,使原本接口不兼容的类能够协同工作。

Adapter 实现 Target 并调用 Adaptee 的对象适配器类图

角色 职责
Target 客户期望使用的目标接口
Adaptee 已有功能的提供者,但接口与客户期望不同
Adapter 对外提供 Target 接口,将调用转换为对 Adaptee 的操作
Client 面向 Target 调用功能

调用过程可理解为:Client → Target.request() → Adapter → Adaptee.specificRequest()。原文的“公接口/母接口”不是标准角色名称,应按目标接口、适配器、被适配者来记。

关键词:接口不兼容、转换接口、复用已有功能。类适配器通过继承复用被适配者;对象适配器通过持有对象并委托调用实现适配,原图展示的是后一种。

详见:适配器模式。

桥接(Bridge)

意图:将抽象部分与实现部分分离,使它们都可以独立变化。

Abstraction 持有 Implementor 并连接两个独立扩展层次

角色 职责
Abstraction 定义客户使用的高层操作,维护 Implementor 引用
RefinedAbstraction 扩展高层功能
Implementor 定义实现层的操作接口
ConcreteImplementor 提供具体实现
Client 使用抽象层的功能

例如“应用类型”和“主题样式”可以分别变化:博客、新闻站、商店都能搭配浅色或深色主题。抽象层持有主题对象,通过组合连接两个变化维度。

关键词:两个独立变化的维度、抽象与实现分离。“抽象部分”是模式中的角色,不等同于 Java 的 abstract 关键字;仅仅定义接口并实现它,也不足以构成桥接。

详见:桥接模式。

组合(Composite)

意图:将对象组合成树形结构以表示“部分—整体”层次,使客户能够一致地使用单个对象和组合对象。

Component 统一 Leaf 与包含子节点的 Composite

角色 职责
Component 声明叶节点和组合节点的共同操作
Leaf 叶节点,不包含子节点
Composite 组合节点,管理子节点,必要时递归调用它们
Client 通过 Component 统一操作节点

例如文件和目录都提供“计算大小”的操作,目录遍历子节点汇总大小,客户不必为每种节点编写独立调用方式。

关键词:树形结构、整体与部分、统一处理叶节点和组合节点。图中把 add()、remove() 放在 Component 上是一种设计选择,也可以仅由 Composite 提供子节点管理操作。“使用对象组合”是普遍设计手段,不等于采用了组合模式。

详见:组合模式。

装饰(Decorator)

意图:动态地给对象添加额外职责,为通过继承扩展功能提供更灵活的替代方式。

Decorator 与被包装对象使用共同的 Component 接口

角色 职责
Component 原对象与装饰对象共同遵循的接口
ConcreteComponent 被添加功能的具体对象
Decorator 持有 Component 引用,并把基础工作委托给它
ConcreteDecorator 在委托调用前后增加行为或状态

例如给基础饮料叠加牛奶、摩卡等配料,每层装饰仍能按饮料接口使用。原图把 ConcreteComponent 标成接口,不宜照抄为固定要求;经典角色中它是提供基础行为的具体组件。

关键词:动态附加职责、层层包装、保持共同接口。装饰模式仍可能用继承定义共同接口,灵活性主要来自对象组合和可叠加的包装。

详见:装饰者模式。

外观(Facade)

意图:为子系统中的一组接口提供统一的高层接口,简化子系统的使用。

Facade 将客户请求转交给多个子系统

角色 职责
Facade 接收较高层请求,安排对子系统的调用
Subsystem 实现具体功能,通常不依赖 Facade
Client 通过外观完成常见任务

例如调用一个 watchMovie(),由家庭影院外观协调投影仪、音响和播放器。

关键词:统一入口、简化复杂子系统调用。统一入口不表示整个系统只能有一个方法,也不要求禁止高级客户直接访问子系统。

详见:外观模式。

享元(Flyweight)

意图:通过共享技术有效支持大量细粒度对象,减少重复存储。

享元工厂按键复用对象并提供共享的享元接口

角色 职责
Flyweight 定义享元接口,操作可以接收外部状态
ConcreteFlyweight 保存可共享的内部状态
UnsharedConcreteFlyweight 使用同一接口,但其对象不参与共享
FlyweightFactory 管理享元,已有匹配对象时复用,否则创建
Client 保存或计算外部状态,并在调用时传入

**内部状态**是可以在多个使用场景中共享的数据;**外部状态**依赖具体使用场景,通常由调用方提供。例如同一字形的绘制数据可以共享,而每次显示的位置不同。

关键词:大量细粒度对象、共享、内部状态与外部状态。原图把共享与非共享享元的状态标注对调了:经典结构中的 ConcreteFlyweight 保存可共享的 intrinsic State,非共享享元可以保存自身所需的完整状态。不能理解为共享对象应保存所有使用场景的状态。

详见:享元模式。

代理(Proxy)

意图:为其他对象提供一个代理,以控制对这个对象的访问。

Proxy 与 RealSubject 提供相同的 Subject 接口

角色 职责
Subject 代理与真实对象共同遵循的接口
RealSubject 提供实际功能的对象
Proxy 在访问真实对象时进行控制,并按需要转发请求
Client 通过 Subject 使用代理或真实对象

常见用途包括远程访问、延迟加载、权限检查等。代理通常需要持有或能够取得真实对象。

关键词:替身、控制访问、远程代理、延迟加载。原文的“主要目的是隔离”过于宽泛,应抓住“控制对真实对象的访问”;“快捷方式”只能作比喻,不能单凭它认定代理模式。

详见:代理模式。

结构型模式对照

原讲义七种结构型模式的意图与速记词

模式 判断时关注什么
适配器 把不兼容接口转换成客户期望的接口
桥接 让两个维度分别扩展,并通过对象引用连接
组合 统一对待树中的单个对象和组合对象
装饰 在保持共同接口的同时叠加职责
外观 为多个子系统提供更简单的高层入口
享元 共享可复用状态,降低大量对象的存储成本
代理 控制对真实对象的访问

例题 2:Web 应用与主题样式

假设现在要创建一个 Web 应用框架,基于此框架能够创建不同的具体 Web 应用,比如博客、新闻网站和网上商店等;并可以为每个 Web 应用创建不同的主题样式,如浅色或深色等。

这一业务需求的类图设计适合采用(①)模式(如下图所示)。其中(②)是客户程序使用的主要接口,维护对主题类型的引用。此模式为(③),体现的最主要的意图是(④)。

WebApplication 与 Theme 分别扩展并通过引用连接的例题类图

选项 第①空:模式 第②空:角色 第③空:分类
A 观察者(Observer) WebApplication 创建型对象模式
B 访问者(Visitor) Blog 结构型对象模式
C 策略(Strategy) Theme 行为型类模式
D 桥接(Bridge) Light 行为型对象模式

第④空选项:

  • A、将抽象部分与其实现部分分离,使它们都可以独立地变化
  • B、动态地给一个对象添加一些额外的职责
  • C、为其他对象提供一种代理以控制对这个对象的访问
  • D、将一个类的接口转换成客户希望的另外一个接口
参考答案与解析

第①空选 D。题目有两个独立变化维度:应用类型与主题样式,桥接将它们分别组织,再通过引用连接。

第②空选 A。题图箭头从 WebApplication 指向 Theme,表示前者维护后者的引用。题目说的“客户程序使用的主要接口”是模式的高层抽象角色,不等于必须声明为 Java 的 interface。

题图中的类 模式角色
WebApplication Abstraction,客户使用的高层抽象
Blog、Store、NewsSite RefinedAbstraction,具体应用类型
Theme Implementor,主题实现接口
Light、Dark ConcreteImplementor,具体主题实现

第③空选 B,桥接是结构型对象模式。第④空选 A;B、C、D 分别对应装饰、代理、适配器。

只看到“主题可替换”可能会想到策略,但题干与类图共同强调两套层次独立扩展,桥接更贴切。不能仅凭“可替换”一个词下结论。

考点四:行为型模式

观察者(Observer)

意图:定义对象间一对多的依赖关系,一个对象状态变化时,所有依赖它的对象得到通知并更新。

Subject 维护观察者并在状态变化时调用更新操作

角色 职责
Subject 管理观察者的订阅、解除订阅和通知
ConcreteSubject 保存观察者关注的状态,在需要时发出通知
Observer 声明更新接口
ConcreteObserver 实现更新行为;拉取数据时可以持有主题引用

关键词:一对多依赖、订阅、状态变化后通知、联动更新。通知可以同步或异步,不必通过消息队列;主题也可以主动推送更新数据。原图的 Observe、ConcreteObserve 分别对应标准名称 Observer、ConcreteObserver。

详见:观察者模式。

访问者(Visitor)

意图:将作用于对象结构中各元素的操作组织起来,使得可以在不改变元素类的前提下定义新操作。

元素通过 accept 接收访问者并回调相应的 visit 操作

角色 职责
Visitor 为各种具体元素类型声明相应的访问操作
ConcreteVisitor 实现这些操作,代表一种具体处理任务
Element 声明 accept(Visitor)
ConcreteElement 实现 accept(),把自身传给访问者的对应操作
ObjectStructure 组织元素,并支持访问者遍历或访问它们

例如毛巾元素的 accept(visitor) 调用 visitor.visitTowel(this),饼干元素调用 visitor.visitCookie(this)。元素知道如何接待访问者,访问者负责执行本次处理任务。

关键词:对象结构较稳定、经常新增操作、accept/visit。新增访问者比较方便;新增元素类型通常会影响访问者接口及已有访问者。原文“抽象具体元素”应为“具体元素”。

详见:访问者模式。

状态(State)

意图:允许对象在内部状态变化时改变它的行为。

Context 将请求委托给当前 State 对象

角色 职责
Context 对外提供操作,持有当前状态对象并向其委托
State 声明与状态相关的行为接口
ConcreteState 实现某个具体状态下的行为

例如售货机在“未投币”“已投币”“售罄”时,对“按购买按钮”的响应不同。状态转换可以由 Context 或具体状态对象控制。

关键词:内部状态变化,同一请求表现不同。状态模式是一种代码组织方式;UML 状态机图是描述状态与转换的建模工具,画了状态机图不代表实现必然采用状态模式。

详见:状态模式。

职责链/责任链(Chain of Responsibility)

意图:让多个对象都有机会处理请求,避免发送者与具体接收者耦合;将处理者连成链,并沿链传递请求。

Handler 及其具体处理者的类图

角色 职责
Handler 定义处理请求的接口,并支持连接或取得后继处理者
ConcreteHandler 处理职责范围内的请求,否则传给后继
Client 将请求交给链的入口

关键词:沿链传递、多个候选处理者、层层审批。例如小额申请由主管处理,大额申请向经理转交。原图只画出类型层次,读图时还应想到对象之间的后继关系;没有处理者接手时,请求可能未被处理。

经典描述通常是“处理或转发”。实际过滤器链可能在处理后继续传递,要结合题目设定,不能认为所有链都在第一个节点执行后结束。

详见:责任链模式。

命令(Command)

意图:将请求封装成对象,从而能够用不同请求对客户进行参数化,支持请求排队、日志记录及可撤销操作。

Invoker 调用 Command,由 ConcreteCommand 操作 Receiver

角色 职责
Command 声明执行请求的接口,如 execute()
ConcreteCommand 绑定接收者与具体动作,实现执行逻辑
Receiver 实际完成业务操作
Invoker 保存或接收命令,并触发执行
Client 创建命令、配置接收者并完成装配

关键词:请求对象化、排队、日志、撤销/重做。要分清“触发命令”的 Invoker 与“执行具体业务”的 Receiver。命令对象使撤销更容易组织,但撤销仍需实现逆操作或保存状态,不会自动获得。

详见:命令模式。

解释器(Interpreter)

意图:为一种语言定义文法表示,并用解释器根据该表示解释语言中的句子。

AbstractExpression 下的终结符与非终结符表达式

角色 职责
AbstractExpression 声明解释操作
TerminalExpression 解释终结符对应的内容
NonTerminalExpression 按文法组合子表达式并解释
Context 提供解释所需的数据、变量环境等上下文
Client 建立表达式结构并调用解释操作

关键词:语言、文法规则、表达式、解释执行。例如用表达式对象组合“价格大于某值并且库存大于零”的规则。原汇总图中的“虚拟机机制”过于宽泛,不能把所有虚拟机都认作解释器模式。

详见:解释器模式。

迭代器(Iterator)

意图:顺序访问聚合对象中的各个元素,而不暴露它的内部表示。

Aggregate 创建 Iterator,遍历逻辑与容器内部结构分离

角色 职责
Iterator 定义访问和遍历元素的接口
ConcreteIterator 实现遍历逻辑并记录当前位置
Aggregate 声明创建迭代器的接口
ConcreteAggregate 返回与自身存储结构匹配的迭代器

关键词:遍历集合、隐藏内部表示、独立的遍历状态。客户通过迭代器访问元素,不必知道容器底层是数组还是链表。原图中的 CreateAggregate、CreateIterator 应按具体聚合 ConcreteAggregate、具体迭代器 ConcreteIterator 理解。

详见:迭代器模式。

中介者(Mediator)

意图:用一个中介对象封装一组对象之间的交互,使各对象不必显式地相互引用,并能独立改变交互规则。

同事对象通过 Mediator 协调交互的类图

角色 职责
Mediator 声明协调同事对象交互的接口
ConcreteMediator 实现协作规则,维护所需的同事对象引用
Colleague 参与协作的对象,通常依赖 Mediator 抽象
ConcreteColleague 实现自身行为,通过中介者参与协调

多个对象直接交互与通过中介者协调的结构对比

关键词:集中协调、减少多对多依赖、通过中介交互。例如对话框中选择框、输入框、按钮的联动规则由统一对象协调。应把需要协调的交互交给中介者,而非把所有业务逻辑都堆入一个类。

详见:中介者模式。

备忘录(Memento)

意图:在不破坏封装的前提下,捕获并在对象外部保存其内部状态,以便将来恢复。

Originator 创建和恢复 Memento,Caretaker 保存备忘录

角色 职责
Originator 原发器/发起者,创建快照并使用快照恢复自身状态
Memento 保存需要恢复的状态,限制外部访问其内容
Caretaker 管理和保存备忘录,不检查或修改其中的内部状态

关键词:保存快照、恢复历史状态、不破坏封装。原图中备忘录的 GetState()、SetState() 是角色协作示意,不应理解为向所有客户公开内部状态;实现需要维护原发器与管理者之间的访问边界。

详见:备忘录模式。

策略(Strategy)

意图:定义一系列算法,分别封装并使它们可以相互替换,让算法独立于使用它的客户变化。

Context 持有 Strategy 并委托执行可替换算法

角色 职责
Strategy 定义各算法共同遵循的接口
ConcreteStrategy 实现某一种具体算法
Context 持有选定策略,并通过策略接口委托工作

例如同一笔订单使用不同折扣算法,客户或配置逻辑选择策略,订单计算过程通过统一接口调用它。

关键词:一组可替换算法、多方案切换。原文把“用具体策略配置执行环境”写进 ConcreteStrategy 的职责,容易混淆;具体策略负责算法,Context 使用该策略。

详见:策略模式。

模板方法(Template Method)

意图:定义算法骨架,将一些步骤延迟到子类,使子类在不改变整体结构的情况下重新定义特定步骤。

AbstractClass 定义模板方法并调用由子类实现的步骤

角色 职责
AbstractClass 实现模板方法、固定整体流程,声明可由子类定制的步骤
ConcreteClass 实现或重写这些步骤,提供特定行为

例如“读取数据 → 处理数据 → 输出结果”是固定流程,不同子类定制读取和处理方式。模板方法可以调用抽象步骤、已有具体方法和钩子方法。

关键词:算法骨架不变、部分步骤由子类实现。它主要通过继承定制步骤,属于行为型类模式;不是原文误写的 Strategy。

详见:模板方法模式。

行为型模式对照

原讲义十一种行为型模式的意图与关键词汇总

题干中的主要诉求 优先考虑
一个对象变化,通知多个依赖者 观察者
元素类型较稳定,为对象结构增加新操作 访问者
同一请求随对象内部状态变化而表现不同 状态
多个候选处理者,让请求沿链传递 职责链
将请求封装成对象,安排执行、排队或撤销 命令
表示文法规则并解释表达式 解释器
遍历元素且隐藏容器内部结构 迭代器
把多个对象的交互规则交给中介集中协调 中介者
保存并恢复内部状态,维持封装 备忘录
把可互换算法分别封装 策略
固定算法骨架,让子类定制步骤 模板方法

例题 3:超市商品结账

假设现在要创建一个简单的超市销售系统,顾客将毛巾、饼干、酸奶等物品(Item)加入购物车(Shopping_Cart),在收银台(Checkout)人工(Manual)或自动(Auto)地将购物车中每个物品的价格汇总到总价格后结账。

这一业务需求的类图(方法略)设计如下图所示,采用了(①)模式。其中(②)定义以一个 Checkout 对象为参数的 accept 操作,由子类实现此 accept 操作。此模式为(③),适用于(④)。

Item 的具体商品与 Checkout 的具体访问者构成结账例题

选项 第①空:模式 第②空:角色 第③空:分类
A 观察者(Observer) Item 创建型对象模式
B 访问者(Visitor) Shopping_Cart 结构型对象模式
C 策略(Strategy) Checkout 行为型类模式
D 桥接器(Bridge) Manual 和 Auto 行为型对象模式

第④空选项:

  • A、必须保存一个对象在某一个时刻的(部分)状态
  • B、想在不明确指定接收者的情况下向多个对象中的一个提交一个请求
  • C、需要对一个对象结构中的对象进行很多不同的并且不相关的操作
  • D、在不同的时刻指定、排列和执行请求
参考答案与解析

第①空选 B。“人工/自动两种方式”本身不足以判断模式,但题干明确给出了以 Checkout 为参数的 accept 操作,这与元素接收访问者的协作方式对应。

第②空选 A。Item 声明 accept(Checkout),具体商品实现它;Checkout 承担访问者角色。

题图中的类 模式角色
Item Element,声明接收访问者的操作
Towel、Cookie、Yogurt ConcreteElement,实现 accept
Checkout Visitor,为具体商品类型声明访问操作
Manual、Auto ConcreteVisitor,执行具体结账操作
Shopping_Cart ObjectStructure,组织商品集合

典型调用过程为:遍历购物车 → 调用商品的 accept(checkout) → 商品回调 checkout 对应的访问方法。访问方法名称在图中省略,例如毛巾可以调用 checkout.visitTowel(this)。

第③空选 D,访问者是行为型对象模式。第④空选 C;A、B、D 分别对应备忘录、职责链、命令的典型适用场景。

这张图省略了方法,单独看两组类层次不足以唯一认定访问者。要结合题干的 accept 参数与角色关系;如果设计仅把可替换的结账算法交给一个策略对象执行,则可能是策略模式。

易混模式怎么区分

容易混淆的模式 判断依据
工厂方法与抽象工厂 前者把具体产品的实例化决定交给子类;后者提供创建一族配套产品的接口。两者可以共同使用,不按创建方法数量机械区分
抽象工厂与建造者 前者强调一族产品的配套关系;后者强调复杂对象的构建步骤与最终表示。不要只数返回对象的数量
适配器与桥接 前者解决已有接口不兼容;后者使两个变化维度能够独立扩展
装饰与代理 二者都可能包装同接口对象;装饰着重叠加职责,代理着重控制访问
外观与中介者 外观为子系统提供简化入口;中介者集中协调参与对象之间的交互规则
策略与状态 结构可能相似;策略表达可替换算法,状态表达对象不同状态下的行为及转换。不能简单以“是否运行时切换”区分
策略与模板方法 策略通常通过组合替换算法对象;模板方法通过继承定制固定流程中的步骤
命令与备忘录 命令保存并组织“要执行的请求”;备忘录保存“某时刻的状态”。撤销功能可以同时使用二者
迭代器与访问者 迭代器解决怎样遍历元素;访问者解决对不同元素执行怎样的操作。二者可以配合使用

类图角色题的读法

图中的英文类名通常表示角色,但实际题目会改成业务名称。先看职责,再映射名称:

图中线索 应检查的角色
持有 Builder,按步骤调用构建方法 Director
实现构建步骤并提供最终产品 ConcreteBuilder
提供高层操作,持有 Implementor 桥接的 Abstraction
声明 accept(Visitor) 访问者模式的 Element
为不同元素提供 visit 操作 Visitor
触发 execute() 命令模式的 Invoker
被具体命令调用,完成实际业务 Receiver
管理订阅者并发出通知 观察者模式的 Subject
创建、恢复快照 备忘录模式的 Originator
只保存快照,不读取内部状态 Caretaker

遇到 Context、Client、Concrete... 等常见名字,不要把它们当成某一个模式独有的关键词。类图省略关系或方法时,还应结合题干补全协作过程。

来源与延伸阅读

评论