### 一、策略模式 (Strategy) **核心思想**: 把可互换的算法/行为抽成独立策略类,运行时由“上下文”对象选择合适的策略;对调用方来说,只关心统一接口,而非具体实现。 ``` ┌───────────────┐ │ Client │ └─────▲─────────┘ │ has-a ┌─────┴─────────┐ implements │ Context │────────────┐ ┌──────────────┐ │ (使用者) │ strategy └─▶│ Strategy A │ └───────────────┘ ├──────────────┤ │ Strategy B │ └──────────────┘ ``` #### Demo:支付策略(Java) ```java // 1. 抽象策略 public interface PayStrategy { void pay(int cents); } // 2. 具体策略 public class AliPay implements PayStrategy { public void pay(int cents) { System.out.println("Alipay ¥" + cents / 100.0); } } public class WxPay implements PayStrategy { public void pay(int cents) { System.out.println("WeChat Pay ¥" + cents / 100.0); } } // 3. 上下文 public class PaymentService { private final PayStrategy strategy; public PaymentService(PayStrategy strategy) { this.strategy = strategy; } public void checkout(int cents) { strategy.pay(cents); } } // 4. 运行时选择策略 public class Demo { public static void main(String[] args) { PaymentService ps1 = new PaymentService(new AliPay()); ps1.checkout(2599); // Alipay ¥25.99 PaymentService ps2 = new PaymentService(new WxPay()); ps2.checkout(4999); // WeChat Pay ¥49.99 } } ``` **要点** - **开放封闭**:新增 PayPal 只需实现 `PayStrategy`,无须改 `PaymentService`。 - **运行期切换**:可根据配置、用户偏好等动态注入不同策略。 ------ ### 二、模板方法模式 (Template Method) **核心思想**: 在抽象父类中定义**算法骨架**(固定执行顺序),把某些可变步骤留给子类重写;调用方只用模板方法,保证流程一致。 ``` Client ───▶ AbstractClass ├─ templateMethod() ←—— 固定流程 │ step1() │ step2() ←—— 抽象,可变 │ step3() └─ hookMethod() ←—— 可选覆盖 ▲ │ extends ┌──────────┴──────────┐ │ ConcreteClassA/B… │ ``` #### Demo:弹窗加载流程(Java) ```java // 1. 抽象模板 public abstract class AbstractDialog { // 模板方法:固定调用顺序,设为 final 防止子类改流程 public final void show() { initLayout(); bindEvent(); beforeDisplay(); // 钩子,可选 display(); afterDisplay(); // 钩子,可选 } // 具体公共步骤 private void initLayout() { System.out.println("加载通用布局文件"); } // 需要子类实现的抽象步骤 protected abstract void bindEvent(); // 钩子方法,默认空实现 protected void beforeDisplay() {} protected void afterDisplay() {} private void display() { System.out.println("弹出对话框"); } } // 2. 子类:登录对话框 public class LoginDialog extends AbstractDialog { @Override protected void bindEvent() { System.out.println("绑定登录按钮事件"); } @Override protected void afterDisplay() { System.out.println("focus 到用户名输入框"); } } // 3. 调用 public class Demo { public static void main(String[] args) { AbstractDialog dialog = new LoginDialog(); dialog.show(); /* 输出: 加载通用布局文件 绑定登录按钮事件 弹出对话框 focus 到用户名输入框 */ } } ``` **要点** - **复用公共流程**:`initLayout()`、`display()` 写一次即可。 - **限制流程顺序**:`show()` 定为 `final`,防止子类乱改步骤。 - **钩子方法**:子类可选择性覆盖(如 `beforeDisplay`)。 ------ ### 关键区别 & 组合用法 | | **策略模式** | **模板方法模式** | | ---------------- | ---------------------------------- | ---------------------------------------- | | **目的** | **横向**扩展——允许算法**并列互换** | **纵向**复用——抽取算法**骨架**,固定顺序 | | **实现方式** | 组合 + 接口 | 继承 + 抽象父类 | | **行为选择时机** | 运行时由外部注入 | 编译期由继承确定 | | **常组合** | 与 **工厂模式**配合选择策略 | 与 **钩子方法**、**回调**一起用 | 在实际项目中,两者经常**组合**: > 折扣计算 **Strategy** → 公共过滤 & 日志 **Template Method** → Spring 容器负责策略注册/发现。 这样即可同时获得“纵向流程复用”+“横向算法可插拔”的双重优势。