5.4 KiB
5.4 KiB
一、策略模式 (Strategy)
核心思想: 把可互换的算法/行为抽成独立策略类,运行时由“上下文”对象选择合适的策略;对调用方来说,只关心统一接口,而非具体实现。
┌───────────────┐
│ Client │
└─────▲─────────┘
│ has-a
┌─────┴─────────┐ implements
│ Context │────────────┐ ┌──────────────┐
│ (使用者) │ strategy └─▶│ Strategy A │
└───────────────┘ ├──────────────┤
│ Strategy B │
└──────────────┘
Demo:支付策略(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)
// 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 容器负责策略注册/发现。
这样即可同时获得“纵向流程复用”+“横向算法可插拔”的双重优势。