学习设计模式时,你是否也遇到过这种情况?
“策略模式和模板方法有什么不同?命令又为什么出现在这里?”
三种模式都有替换行为的感觉,很容易一直混淆。
先说重点。
策略会整体替换算法,
模板方法固定骨架,只替换部分步骤,
命令则把“要执行的行为”封装成对象,之后再执行。
掌握这一句,就等于完成了一半。今天我们结合代码彻底区分这三种模式。
三种模式,一眼看懂区别
先用表格整理整体概念。
| 分类 | 策略模式 | 模板方法 | 命令模式 |
|---|---|---|---|
| 核心 | 整体替换算法 | 固定骨架,替换部分步骤 | 将行为封装成对象 |
| 复用方式 | 组合(委托) | 继承 | 组合(委托) |
| 替换时机 | 运行时 | 编译时(继承) | 运行时 |
| 主要关注点 | “如何计算” | “顺序相同、细节不同时” | “何时执行什么” |
三种模式容易混淆,原因就在这里。
因为它们的目的都是分离会变化的部分。
但分离方式和目的不同。下面逐一拆解。
策略模式整体替换算法
策略模式将多种完成同一任务的方式分别建成独立对象,需要时再替换。
以支付方式为例就很合适。
刷卡、KakaoPay、银行转账:目标(支付)相同,但方法不同。
protocol PayStrategy {
func pay(_ amount: Int) -> String
}
struct CardPay: PayStrategy {
func pay(_ amount: Int) -> String { "刷卡支付 \(amount)" }
}
struct KakaoPay: PayStrategy {
func pay(_ amount: Int) -> String { "使用 KakaoPay 支付 \(amount)" }
}
var strategy: PayStrategy = CardPay()
print(strategy.pay(10000))
// 输出:刷卡支付 10000
关键在于,运行时将 strategy 变量改为 KakaoPay(),就能整体替换行为。
重点是通过组合(composition)委托,而不是使用继承。
模板方法保留骨架,只替换部分内容
模板方法的思路稍有不同。
整体流程顺序固定,其中一些步骤由子类填充。
想想煮拉面:烧水 → 加入食材 → 完成。顺序相同,只有“加入食材”这一步不同。
class Ramen {
func cook() { // 这就是模板方法
boilWater()
addIngredients() // 只在子类中替换这一步
print("完成!")
}
func boilWater() { print("烧水") }
func addIngredients() { print("基础食材") }
}
class CheeseRamen: Ramen {
override func addIngredients() { print("加入奶酪") }
}
CheeseRamen().cook()
// 输出:烧水 / 加入奶酪 / 完成!
它与策略模式的关键区别是使用继承,并且只替换部分步骤。
流程(cook)由父类掌控。
命令模式封装要执行的行为
命令的关注点与前两者完全不同。
它关注的不是如何计算,而是将行为封装成对象,以便之后执行、取消或加入队列。
想象遥控器按钮就很容易。按钮保存“开灯”这一命令,按下时才执行。
protocol Command { func execute() }
struct LightOn: Command {
func execute() { print("开灯") }
}
let button: Command = LightOn()
button.execute() // 由我决定执行时机
// 输出:开灯
由于请求被封装成对象,因此很自然就能加入撤销(undo)、日志记录和任务队列。
简单来说,策略用于选择方法,命令用于保存和处理请求。
何时使用,何时避免
在实际工作中,我会按以下标准选择。
- 策略模式:同一目标有多个算法,且需要在运行时切换时(排序方式、折扣策略、支付方式)
- 模板方法:处理顺序始终相同,只有部分步骤不同时(数据解析流程、游戏回合推进)
- 命令:需要延迟、取消、重做或排队执行时(撤销、任务调度器)
反过来,也有应该避免的信号。
如果只有两三个分支,今后也不会增加,就不要强行套用模式。简单的 if 反而更易读。
尤其模板方法受继承限制;如果重视灵活性,优先考虑策略模式(组合)通常更稳妥。
面试时可以这样回答
问:策略模式和模板方法最大的区别是什么?
策略通过组合(委托)在运行时替换整个算法;模板方法通过继承固定骨架,只重写部分步骤。策略更灵活,模板方法更擅长控制流程。
问:命令模式和策略的区别在哪里?
策略专注于选择方法;命令将请求本身封装成对象,从而支持撤销、排队和日志记录等操作。两者分别关注算法选择与请求管理。
即使三种模式看起来相似,只要从分离什么、为何分离出发,就能清楚区分。
策略是方法,模板方法是流程中的空缺,命令是请求本身。记住这三个词,以后就不会再混淆。祝你学习愉快!

