设计模式分类、类图与考题解析¶
根据用户提供的软件设计师设计模式讲义整理,覆盖 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 种模式之内,不能把它和工厂方法混算。
做题顺序:先看意图,再找结构证据¶
- 判断变化点:题目是在更换创建方式、组合结构,还是算法与交互行为。
- 用关键词缩小范围,例如“产品族”“分步构建”“整体与部分”“一对多通知”。
- 对照类图:谁继承谁、谁持有谁、谁调用谁,关键方法定义在哪个角色上。
- 再回答模式分类和具体角色,避免仅凭类名或单个词下结论。
“可撤销”可能涉及命令或备忘录,“两组继承结构”也不必然是桥接。关键词提供线索,意图与协作关系决定答案。
考点二:创建型模式¶
抽象工厂(Abstract Factory)¶
意图:提供创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。
| 角色 | 职责 |
|---|---|
| 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)¶
意图:将一个复杂对象的构建与它的表示分离,使同样的构建过程可以创建不同的表示。
| 角色 | 职责 |
|---|---|
| Builder | 声明构建产品各部分的步骤接口 |
| ConcreteBuilder | 实现各步骤,构建并装配产品,提供取得结果的方法 |
| Director | 通过 Builder 接口安排构建步骤及其顺序 |
| Product | 最终构建出的复杂对象 |
“表示”可以理解为最终对象由哪些部件组成、各部件如何配置。相同的“制作主餐 → 配饮料 → 配玩具”流程,可以由不同建造者完成,得到不同套餐。
关键词:复杂对象、分步构建、同一流程得到不同表示。Product 是构建结果,不能把它和负责构建的 ConcreteBuilder 混为一谈;现代实现可以由客户端直接编排步骤,但本讲义题目采用含 Director 的经典结构。
详见:建造者模式。
创建型模式对照¶
| 题干最有区分度的信息 | 优先考虑 |
|---|---|
| 需要一套配套产品,例如按钮和复选框都使用同一种风格 | 抽象工厂 |
| 由子类决定创建哪种具体产品 | 工厂方法 |
| 按同样步骤构建复杂对象,但部件或结果表示不同 | 建造者 |
| 复制一个已有实例得到新对象 | 原型 |
| 控制唯一实例并提供统一访问点 | 单例 |
原图将工厂方法简记为“动态生产对象”,区分度不够:多种创建型模式都能在运行时创建对象,判断工厂方法时应找“由子类决定”。
例题 1:儿童套餐的制作¶
某快餐厅主要制作并出售儿童套餐,一般包括主餐(各类比萨)、饮料和玩具,其餐品种类可能不同,但制作过程相同。前台服务员(Waiter)调度厨师制作套餐。欲开发一软件,实现该制作过程,设计如下所示类图。
该设计采用(①)模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。其中,(②)构造一个使用 Builder 接口的对象。该模式属于(③)模式,该模式适用于(④)的情况。
| 选项 | 第①空:模式 | 第②空:角色 | 第③空:分类 |
|---|---|---|---|
| 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)¶
意图:把一个类的接口转换成客户希望的另一个接口,使原本接口不兼容的类能够协同工作。
| 角色 | 职责 |
|---|---|
| Target | 客户期望使用的目标接口 |
| Adaptee | 已有功能的提供者,但接口与客户期望不同 |
| Adapter | 对外提供 Target 接口,将调用转换为对 Adaptee 的操作 |
| Client | 面向 Target 调用功能 |
调用过程可理解为:Client → Target.request() → Adapter → Adaptee.specificRequest()。原文的“公接口/母接口”不是标准角色名称,应按目标接口、适配器、被适配者来记。
关键词:接口不兼容、转换接口、复用已有功能。类适配器通过继承复用被适配者;对象适配器通过持有对象并委托调用实现适配,原图展示的是后一种。
详见:适配器模式。
桥接(Bridge)¶
意图:将抽象部分与实现部分分离,使它们都可以独立变化。
| 角色 | 职责 |
|---|---|
| Abstraction | 定义客户使用的高层操作,维护 Implementor 引用 |
| RefinedAbstraction | 扩展高层功能 |
| Implementor | 定义实现层的操作接口 |
| ConcreteImplementor | 提供具体实现 |
| Client | 使用抽象层的功能 |
例如“应用类型”和“主题样式”可以分别变化:博客、新闻站、商店都能搭配浅色或深色主题。抽象层持有主题对象,通过组合连接两个变化维度。
关键词:两个独立变化的维度、抽象与实现分离。“抽象部分”是模式中的角色,不等同于 Java 的 abstract 关键字;仅仅定义接口并实现它,也不足以构成桥接。
详见:桥接模式。
组合(Composite)¶
意图:将对象组合成树形结构以表示“部分—整体”层次,使客户能够一致地使用单个对象和组合对象。
| 角色 | 职责 |
|---|---|
| Component | 声明叶节点和组合节点的共同操作 |
| Leaf | 叶节点,不包含子节点 |
| Composite | 组合节点,管理子节点,必要时递归调用它们 |
| Client | 通过 Component 统一操作节点 |
例如文件和目录都提供“计算大小”的操作,目录遍历子节点汇总大小,客户不必为每种节点编写独立调用方式。
关键词:树形结构、整体与部分、统一处理叶节点和组合节点。图中把 add()、remove() 放在 Component 上是一种设计选择,也可以仅由 Composite 提供子节点管理操作。“使用对象组合”是普遍设计手段,不等于采用了组合模式。
详见:组合模式。
装饰(Decorator)¶
意图:动态地给对象添加额外职责,为通过继承扩展功能提供更灵活的替代方式。
| 角色 | 职责 |
|---|---|
| Component | 原对象与装饰对象共同遵循的接口 |
| ConcreteComponent | 被添加功能的具体对象 |
| Decorator | 持有 Component 引用,并把基础工作委托给它 |
| ConcreteDecorator | 在委托调用前后增加行为或状态 |
例如给基础饮料叠加牛奶、摩卡等配料,每层装饰仍能按饮料接口使用。原图把 ConcreteComponent 标成接口,不宜照抄为固定要求;经典角色中它是提供基础行为的具体组件。
关键词:动态附加职责、层层包装、保持共同接口。装饰模式仍可能用继承定义共同接口,灵活性主要来自对象组合和可叠加的包装。
详见:装饰者模式。
外观(Facade)¶
意图:为子系统中的一组接口提供统一的高层接口,简化子系统的使用。
| 角色 | 职责 |
|---|---|
| Facade | 接收较高层请求,安排对子系统的调用 |
| Subsystem | 实现具体功能,通常不依赖 Facade |
| Client | 通过外观完成常见任务 |
例如调用一个 watchMovie(),由家庭影院外观协调投影仪、音响和播放器。
关键词:统一入口、简化复杂子系统调用。统一入口不表示整个系统只能有一个方法,也不要求禁止高级客户直接访问子系统。
详见:外观模式。
享元(Flyweight)¶
意图:通过共享技术有效支持大量细粒度对象,减少重复存储。
| 角色 | 职责 |
|---|---|
| Flyweight | 定义享元接口,操作可以接收外部状态 |
| ConcreteFlyweight | 保存可共享的内部状态 |
| UnsharedConcreteFlyweight | 使用同一接口,但其对象不参与共享 |
| FlyweightFactory | 管理享元,已有匹配对象时复用,否则创建 |
| Client | 保存或计算外部状态,并在调用时传入 |
**内部状态**是可以在多个使用场景中共享的数据;**外部状态**依赖具体使用场景,通常由调用方提供。例如同一字形的绘制数据可以共享,而每次显示的位置不同。
关键词:大量细粒度对象、共享、内部状态与外部状态。原图把共享与非共享享元的状态标注对调了:经典结构中的 ConcreteFlyweight 保存可共享的 intrinsic State,非共享享元可以保存自身所需的完整状态。不能理解为共享对象应保存所有使用场景的状态。
详见:享元模式。
代理(Proxy)¶
意图:为其他对象提供一个代理,以控制对这个对象的访问。
| 角色 | 职责 |
|---|---|
| Subject | 代理与真实对象共同遵循的接口 |
| RealSubject | 提供实际功能的对象 |
| Proxy | 在访问真实对象时进行控制,并按需要转发请求 |
| Client | 通过 Subject 使用代理或真实对象 |
常见用途包括远程访问、延迟加载、权限检查等。代理通常需要持有或能够取得真实对象。
关键词:替身、控制访问、远程代理、延迟加载。原文的“主要目的是隔离”过于宽泛,应抓住“控制对真实对象的访问”;“快捷方式”只能作比喻,不能单凭它认定代理模式。
详见:代理模式。
结构型模式对照¶
| 模式 | 判断时关注什么 |
|---|---|
| 适配器 | 把不兼容接口转换成客户期望的接口 |
| 桥接 | 让两个维度分别扩展,并通过对象引用连接 |
| 组合 | 统一对待树中的单个对象和组合对象 |
| 装饰 | 在保持共同接口的同时叠加职责 |
| 外观 | 为多个子系统提供更简单的高层入口 |
| 享元 | 共享可复用状态,降低大量对象的存储成本 |
| 代理 | 控制对真实对象的访问 |
例题 2:Web 应用与主题样式¶
假设现在要创建一个 Web 应用框架,基于此框架能够创建不同的具体 Web 应用,比如博客、新闻网站和网上商店等;并可以为每个 Web 应用创建不同的主题样式,如浅色或深色等。
这一业务需求的类图设计适合采用(①)模式(如下图所示)。其中(②)是客户程序使用的主要接口,维护对主题类型的引用。此模式为(③),体现的最主要的意图是(④)。
| 选项 | 第①空:模式 | 第②空:角色 | 第③空:分类 |
|---|---|---|---|
| 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 | 管理观察者的订阅、解除订阅和通知 |
| ConcreteSubject | 保存观察者关注的状态,在需要时发出通知 |
| Observer | 声明更新接口 |
| ConcreteObserver | 实现更新行为;拉取数据时可以持有主题引用 |
关键词:一对多依赖、订阅、状态变化后通知、联动更新。通知可以同步或异步,不必通过消息队列;主题也可以主动推送更新数据。原图的 Observe、ConcreteObserve 分别对应标准名称 Observer、ConcreteObserver。
详见:观察者模式。
访问者(Visitor)¶
意图:将作用于对象结构中各元素的操作组织起来,使得可以在不改变元素类的前提下定义新操作。
| 角色 | 职责 |
|---|---|
| Visitor | 为各种具体元素类型声明相应的访问操作 |
| ConcreteVisitor | 实现这些操作,代表一种具体处理任务 |
| Element | 声明 accept(Visitor) |
| ConcreteElement | 实现 accept(),把自身传给访问者的对应操作 |
| ObjectStructure | 组织元素,并支持访问者遍历或访问它们 |
例如毛巾元素的 accept(visitor) 调用 visitor.visitTowel(this),饼干元素调用 visitor.visitCookie(this)。元素知道如何接待访问者,访问者负责执行本次处理任务。
关键词:对象结构较稳定、经常新增操作、accept/visit。新增访问者比较方便;新增元素类型通常会影响访问者接口及已有访问者。原文“抽象具体元素”应为“具体元素”。
详见:访问者模式。
状态(State)¶
意图:允许对象在内部状态变化时改变它的行为。
| 角色 | 职责 |
|---|---|
| Context | 对外提供操作,持有当前状态对象并向其委托 |
| State | 声明与状态相关的行为接口 |
| ConcreteState | 实现某个具体状态下的行为 |
例如售货机在“未投币”“已投币”“售罄”时,对“按购买按钮”的响应不同。状态转换可以由 Context 或具体状态对象控制。
关键词:内部状态变化,同一请求表现不同。状态模式是一种代码组织方式;UML 状态机图是描述状态与转换的建模工具,画了状态机图不代表实现必然采用状态模式。
详见:状态模式。
职责链/责任链(Chain of Responsibility)¶
意图:让多个对象都有机会处理请求,避免发送者与具体接收者耦合;将处理者连成链,并沿链传递请求。
| 角色 | 职责 |
|---|---|
| Handler | 定义处理请求的接口,并支持连接或取得后继处理者 |
| ConcreteHandler | 处理职责范围内的请求,否则传给后继 |
| Client | 将请求交给链的入口 |
关键词:沿链传递、多个候选处理者、层层审批。例如小额申请由主管处理,大额申请向经理转交。原图只画出类型层次,读图时还应想到对象之间的后继关系;没有处理者接手时,请求可能未被处理。
经典描述通常是“处理或转发”。实际过滤器链可能在处理后继续传递,要结合题目设定,不能认为所有链都在第一个节点执行后结束。
详见:责任链模式。
命令(Command)¶
意图:将请求封装成对象,从而能够用不同请求对客户进行参数化,支持请求排队、日志记录及可撤销操作。
| 角色 | 职责 |
|---|---|
| Command | 声明执行请求的接口,如 execute() |
| ConcreteCommand | 绑定接收者与具体动作,实现执行逻辑 |
| Receiver | 实际完成业务操作 |
| Invoker | 保存或接收命令,并触发执行 |
| Client | 创建命令、配置接收者并完成装配 |
关键词:请求对象化、排队、日志、撤销/重做。要分清“触发命令”的 Invoker 与“执行具体业务”的 Receiver。命令对象使撤销更容易组织,但撤销仍需实现逆操作或保存状态,不会自动获得。
详见:命令模式。
解释器(Interpreter)¶
意图:为一种语言定义文法表示,并用解释器根据该表示解释语言中的句子。
| 角色 | 职责 |
|---|---|
| AbstractExpression | 声明解释操作 |
| TerminalExpression | 解释终结符对应的内容 |
| NonTerminalExpression | 按文法组合子表达式并解释 |
| Context | 提供解释所需的数据、变量环境等上下文 |
| Client | 建立表达式结构并调用解释操作 |
关键词:语言、文法规则、表达式、解释执行。例如用表达式对象组合“价格大于某值并且库存大于零”的规则。原汇总图中的“虚拟机机制”过于宽泛,不能把所有虚拟机都认作解释器模式。
详见:解释器模式。
迭代器(Iterator)¶
意图:顺序访问聚合对象中的各个元素,而不暴露它的内部表示。
| 角色 | 职责 |
|---|---|
| Iterator | 定义访问和遍历元素的接口 |
| ConcreteIterator | 实现遍历逻辑并记录当前位置 |
| Aggregate | 声明创建迭代器的接口 |
| ConcreteAggregate | 返回与自身存储结构匹配的迭代器 |
关键词:遍历集合、隐藏内部表示、独立的遍历状态。客户通过迭代器访问元素,不必知道容器底层是数组还是链表。原图中的 CreateAggregate、CreateIterator 应按具体聚合 ConcreteAggregate、具体迭代器 ConcreteIterator 理解。
详见:迭代器模式。
中介者(Mediator)¶
意图:用一个中介对象封装一组对象之间的交互,使各对象不必显式地相互引用,并能独立改变交互规则。
| 角色 | 职责 |
|---|---|
| Mediator | 声明协调同事对象交互的接口 |
| ConcreteMediator | 实现协作规则,维护所需的同事对象引用 |
| Colleague | 参与协作的对象,通常依赖 Mediator 抽象 |
| ConcreteColleague | 实现自身行为,通过中介者参与协调 |
关键词:集中协调、减少多对多依赖、通过中介交互。例如对话框中选择框、输入框、按钮的联动规则由统一对象协调。应把需要协调的交互交给中介者,而非把所有业务逻辑都堆入一个类。
详见:中介者模式。
备忘录(Memento)¶
意图:在不破坏封装的前提下,捕获并在对象外部保存其内部状态,以便将来恢复。
| 角色 | 职责 |
|---|---|
| Originator | 原发器/发起者,创建快照并使用快照恢复自身状态 |
| Memento | 保存需要恢复的状态,限制外部访问其内容 |
| Caretaker | 管理和保存备忘录,不检查或修改其中的内部状态 |
关键词:保存快照、恢复历史状态、不破坏封装。原图中备忘录的 GetState()、SetState() 是角色协作示意,不应理解为向所有客户公开内部状态;实现需要维护原发器与管理者之间的访问边界。
详见:备忘录模式。
策略(Strategy)¶
意图:定义一系列算法,分别封装并使它们可以相互替换,让算法独立于使用它的客户变化。
| 角色 | 职责 |
|---|---|
| Strategy | 定义各算法共同遵循的接口 |
| ConcreteStrategy | 实现某一种具体算法 |
| Context | 持有选定策略,并通过策略接口委托工作 |
例如同一笔订单使用不同折扣算法,客户或配置逻辑选择策略,订单计算过程通过统一接口调用它。
关键词:一组可替换算法、多方案切换。原文把“用具体策略配置执行环境”写进 ConcreteStrategy 的职责,容易混淆;具体策略负责算法,Context 使用该策略。
详见:策略模式。
模板方法(Template Method)¶
意图:定义算法骨架,将一些步骤延迟到子类,使子类在不改变整体结构的情况下重新定义特定步骤。
| 角色 | 职责 |
|---|---|
| AbstractClass | 实现模板方法、固定整体流程,声明可由子类定制的步骤 |
| ConcreteClass | 实现或重写这些步骤,提供特定行为 |
例如“读取数据 → 处理数据 → 输出结果”是固定流程,不同子类定制读取和处理方式。模板方法可以调用抽象步骤、已有具体方法和钩子方法。
关键词:算法骨架不变、部分步骤由子类实现。它主要通过继承定制步骤,属于行为型类模式;不是原文误写的 Strategy。
详见:模板方法模式。
行为型模式对照¶
| 题干中的主要诉求 | 优先考虑 |
|---|---|
| 一个对象变化,通知多个依赖者 | 观察者 |
| 元素类型较稳定,为对象结构增加新操作 | 访问者 |
| 同一请求随对象内部状态变化而表现不同 | 状态 |
| 多个候选处理者,让请求沿链传递 | 职责链 |
| 将请求封装成对象,安排执行、排队或撤销 | 命令 |
| 表示文法规则并解释表达式 | 解释器 |
| 遍历元素且隐藏容器内部结构 | 迭代器 |
| 把多个对象的交互规则交给中介集中协调 | 中介者 |
| 保存并恢复内部状态,维持封装 | 备忘录 |
| 把可互换算法分别封装 | 策略 |
| 固定算法骨架,让子类定制步骤 | 模板方法 |
例题 3:超市商品结账¶
假设现在要创建一个简单的超市销售系统,顾客将毛巾、饼干、酸奶等物品(Item)加入购物车(Shopping_Cart),在收银台(Checkout)人工(Manual)或自动(Auto)地将购物车中每个物品的价格汇总到总价格后结账。
这一业务需求的类图(方法略)设计如下图所示,采用了(①)模式。其中(②)定义以一个 Checkout 对象为参数的 accept 操作,由子类实现此 accept 操作。此模式为(③),适用于(④)。
| 选项 | 第①空:模式 | 第②空:角色 | 第③空:分类 |
|---|---|---|---|
| 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... 等常见名字,不要把它们当成某一个模式独有的关键词。类图省略关系或方法时,还应结合题干补全协作过程。
来源与延伸阅读¶
- 面向对象技术课件提要与回顾:本章五篇课件的阅读入口与复习要求。
- 原材料:用户提供的希赛软件设计师“设计模式”讲义(课件 ID:161757),已于 2026-09-28 与 Web Clipper 提取内容核对,配图保留自原材料并保存为本地文件。
- 分类与术语采用 GoF《设计模式:可复用面向对象软件的基础》的 23 种模式体系。
- 设计模式学习总览:各模式的详细笔记、类结构和 Java 示例入口。
- UML 图、关系与读图练习:先复习继承、实现、关联等图形符号,再分析模式协作关系。






























