策略模式(Strategy)和模板方法模式(Template Method)是面试中“撞车率”最高的两个模式。它们都在解决“算法封装”和“扩展性”的问题,代码看起也很像(都用了多态),但本质截然不同。
一句话总结核心区别:
策略模式是“换脑子”(组合/整体替换),模板模式是“填空题”(继承/局部定制)。
1. 核心维度的深度对比
| 维度 | 模板方法模式 (Template Method) | 策略模式 (Strategy Pattern) |
|---|---|---|
| 复用机制 | 继承 (Inheritance) | 组合 (Composition) |
| 关系 | Is-A (子类是父类的一种) | Has-A (Context 持有一个 Strategy) |
| 灵活性 | 低 (编译期决定,对象创建后无法改变行为) | 高 (运行期决定,随时可以用 SetXXX 切换) |
| 算法粒度 | 部分定制 (大流程不变,只改小步骤) | 整体替换 (整个算法逻辑全部换掉) |
| 控制权 | 父类控制子类 (好莱坞原则) | 客户端选择具体策略 (委托机制) |
| 修改成本 | 需要修改继承树,耦合度相对较高 | 新增类即可,无需修改 Context,耦合极低 |
2. 代码结构的直观差异 (C++ 视角)
A. 模板方法:填空题
- 场景: 泡茶和泡咖啡。
- 逻辑: 烧水 -> (放入原料) -> 倒水 -> (加调料)。
- 特点: 流程是死的,具体放什么原料是留空的。、
class Beverage { // 模板
public:// 流程被父类“锁死”了,子类动不了void MakeBeverage() {BoilWater();Brew(); // 填空 APourInCup();AddCondiments(); // 填空 B}
protected:virtual void Brew() = 0; // 留给子类实现virtual void AddCondiments() = 0; // 留给子类实现
private:void BoilWater() { ... } // 公用逻辑void PourInCup() { ... } // 公用逻辑
};// 使用:一旦你 new Tea(),它这辈子就是茶,变不成咖啡
Beverage* b = new Tea();
b->MakeBeverage();
B. 策略模式:换脑子
- 场景: 手机导航。
- 逻辑: 给我规划一条路。
- 特点: 我不管中间步骤(怎么左转怎么右转),我只关注整体算法的选择(是走最短路径,还是走最少收费)。
class IRouteStrategy { // 策略接口
public:virtual void BuildRoute() = 0;
};class Navigator { // 上下文IRouteStrategy* strategy; // 持有接口
public:void SetStrategy(IRouteStrategy* s) { strategy = s; }void Build() { strategy->BuildRoute(); } // 委托
};// 使用:随时可以切
Navigator nav;
nav.SetStrategy(new FastestRoute()); // 只要快
nav.Build();
nav.SetStrategy(new CheapestRoute()); // 没钱了,换省钱路线
nav.Build();
3. 生动比喻
- 模板方法模式 就像 “做试卷”:
- 试卷的题目(算法骨架)是老师印好的,不能改。
- 你只能在括号里填上你的答案(子类重写虚函数)。
- 你不能把语文卷子变成数学卷子。
- 策略模式 就像 “USB 插拔”:
- 电脑(Context)有一个 USB 接口。
- 你可以插 U 盘(策略A),也可以插 鼠标(策略B),也可以插 键盘(策略C)。
- 只要符合 USB 协议(接口),想换什么换什么,想什么时候换就什么时候换。
4. 什么时候用哪个?(选型指南)
选 模板方法 (Template Method) 当:
- 你有一个固定的操作流程,只有其中几个步骤需要变化。
- 你想把公共的代码抽离到父类中,避免代码重复。
- 核心在于: 你想控制子类的行为流程(父类是导演)。
选 策略模式 (Strategy) 当:
- 你需要动态地切换算法(比如游戏里换武器,商城里换打折方案)。
- 你有许多相似的类,唯一的区别只是它们解决问题的方式(算法)不同。
- 你想隐藏算法的具体实现细节,只暴露一个接口。
- 核心在于: 你想让算法独立于使用它的客户而变化(彻底解耦)。
5. 高级思考:两者能结合吗?
能!而且非常常见。
在策略模式的具体策略类内部,为了实现那个具体的算法,可能会使用模板方法模式。
- 例子:
- 策略层: 游戏中有
近战策略和远程策略(Strategy)。 - 模板层: 在
近战策略内部,攻击流程是:举起武器->挥舞(可变)->造成伤害。这又是一个模板方法。
- 策略层: 游戏中有
总结: 策略模式是大局观(整体替换),模板模式是微操(细节定制)。