【设计模式】策略模式:告别 if-else 地狱
当业务分支越来越多,if-else 会让代码难以维护。本文用前端真实场景讲清策略模式:抽象策略、上下文调用、动态切换与落地注意事项。
9 分钟阅读
小明
【设计模式】策略模式:告别 if-else 地狱
你一定见过这种代码:它最初只有 2 个分支,但半年后变成 12 个。谁改谁怕。
把“变化的算法”从主流程里拆出去,让主流程只负责调度。
1. 策略模式是什么
三个角色:
- Strategy(策略):同一接口,不同实现
- Concrete Strategy(具体策略):真正的算法/业务逻辑
- Context(上下文):根据条件选择并执行策略
它的价值是:
- 开闭原则:新增策略,不动原有主流程
- 单一职责:每个策略只做一件事
2. 一个支付场景的落地例子
type Order = { id: string; amount: number }
type PayResult = { success: boolean; message: string }
interface PayStrategy {
pay(order: Order): Promise<PayResult>
}
class WechatPayStrategy implements PayStrategy {
async pay(order: Order): Promise<PayResult> {
return { success: true, message: `微信支付成功: ${order.id}` }
}
}
class AlipayStrategy implements PayStrategy {
async pay(order: Order): Promise<PayResult> {
return { success: true, message: `支付宝支付成功: ${order.id}` }
}
}
class BankPayStrategy implements PayStrategy {
async pay(order: Order): Promise<PayResult> {
return { success: true, message: `银行卡支付成功: ${order.id}` }
}
}
class PayContext {
constructor(private strategy: PayStrategy) {}
setStrategy(strategy: PayStrategy) {
this.strategy = strategy
}
async execute(order: Order) {
return this.strategy.pay(order)
}
}
调用时:
const strategyMap = {
wechat: new WechatPayStrategy(),
alipay: new AlipayStrategy(),
bank: new BankPayStrategy()
}
const context = new PayContext(strategyMap.wechat)
await context.execute({ id: 'A1001', amount: 199 })
新增 applePay 时,只需要:
- 新建一个
ApplePayStrategy - 在映射表注册
主流程不需要再加一层 else if。
3. 什么时候该用策略模式
适合:
- 同一个业务动作有多种实现
- 分支会持续增加
- 需要按配置或运行时动态切换
不适合:
- 只有 2 个稳定分支,且未来基本不会变
- 过度抽象导致代码跳转层级过深
4. 前端里常见的策略模式场景
- 表单校验规则(不同字段不同校验策略)
- 价格计算(会员价/活动价/券后价)
- 埋点上报(不同平台不同上报实现)
- 列表排序(按时间/热度/权重)
5. 两个实践建议
- 先接口再实现:先稳定策略接口,避免后续频繁改 Context。
- 策略注册集中管理:建议用
strategyMap统一注册,避免分散在各处。
6. 策略模式的几个常见问题
问题 1:如果每个策略都需要初始化参数呢?
// 不推荐:每次都 new,容易浪费资源
const pay = () => {
const strategy = new WechatPayStrategy(config.wechat)
return context.execute(strategy, order)
}
// 推荐:工厂函数 + 单例缓存
const strategyFactory = new Map<string, PayStrategy>()
function getStrategy(type: string): PayStrategy {
if (!strategyFactory.has(type)) {
if (type === 'wechat') {
strategyFactory.set(type, new WechatPayStrategy(config.wechat))
} else if (type === 'alipay') {
strategyFactory.set(type, new AlipayStrategy(config.alipay))
}
}
return strategyFactory.get(type)!
}
问题 2:策略之间有通用逻辑怎么办?
使用继承或组合:
// 继承共同基类
abstract class BasePayStrategy implements PayStrategy {
protected async log(msg: string) {
console.log(`[Pay] ${msg}`)
}
abstract pay(order: Order): Promise<PayResult>
}
class WechatPayStrategy extends BasePayStrategy {
async pay(order: Order): Promise<PayResult> {
await this.log('开始微信支付')
// 实现具体逻辑
return { success: true, message: `微信支付成功` }
}
}
问题 3:怎么监控/统计各策略的执行情况?
装饰器模式包装:
class MonitoredPayStrategy implements PayStrategy {
constructor(private innerStrategy: PayStrategy) {}
async pay(order: Order): Promise<PayResult> {
const start = Date.now()
try {
const result = await this.innerStrategy.pay(order)
console.log(`支付成功,耗时 ${Date.now() - start}ms`)
return result
} catch (error) {
console.error(`支付失败,耗时 ${Date.now() - start}ms`, error)
throw error
}
}
}
// 使用:
const monthtored = new MonitoredPayStrategy(new WechatPayStrategy())
await context.execute(monthtored, order)
策略模式不是为了“炫技”,而是为了把变化隔离。
当你发现 if-else 持续膨胀、每次加分支都要改主流程时,基本就是策略模式该上场的信号。