策略模式是一种常见的行为型设计模式,常用于替代大量冗余的 if-else
或 switch-case
结构。它通过将一系列可互换的策略(行为)封装起来,使代码结构更清晰、可扩展性更强。
一、什么是策略模式?
策略模式(Strategy Pattern) 是指将一组算法或操作逻辑抽象为独立的"策略对象",根据上下文条件选择某一种策略执行。
一句话概括:把繁杂的 if-else 判断逻辑,变成"查表执行"。
二、策略模式结构图
plaintext
┌────────────┐
│ Context │ ← 运行环境,持有策略并调用
└────┬───────┘
│
▼
┌────────────┐
│ Strategy A │ ← 实现某个行为
└────────────┘
┌────────────┐
│ Strategy B │ ← 实现另一个行为
└────────────┘
在 JavaScript/TypeScript 中,我们通常用对象映射(Map)来实现策略模式。
三、为什么要使用策略模式?
问题 | 策略模式解决方案 |
---|---|
逻辑混乱 | 将不同逻辑解耦封装 |
if/else 重复臃肿 | 使用映射对象替代判断 |
新增/修改逻辑易出错 | 可扩展、遵循开闭原则 |
单元测试困难 | 策略单元独立、易测试 |
四、策略模式实战案例
✅ 1. 表单校验策略
ts
const validateMap = {
email: (val: string) => /\S+@\S+\.\S+/.test(val),
phone: (val: string) => /^1\d{10}$/.test(val),
username: (val: string) => val.length >= 3
};
function validate(type: string, value: string) {
const fn = validateMap[type];
return fn ? fn(value) : false;
}
✅ 2. Vue
动态视图策略
ts
// strategies.ts
export const renderMap = {
list: () => <ListView />,
card: () => <CardView />,
table: () => <TableView />
}
vue
<template>
<component :is="renderMap[viewMode]" />
</template>
🔁 五、从 if-else 到策略模式
原始写法(冗余):
ts
function parseFile(type, content) {
if (type === 'json') return JSON.parse(content)
if (type === 'xml') return parseXML(content)
if (type === 'csv') return parseCSV(content)
throw new Error('Unknown type')
}
策略模式写法(优雅):
ts
const parsers = {
json: JSON.parse,
xml: parseXML,
csv: parseCSV
}
function parseFile(type: string, content: string) {
const fn = parsers[type]
if (!fn) throw new Error('Unknown type')
return fn(content)
}
🧠 六、策略模式适用场景
-
多分支逻辑(if/else 或 switch)
-
规则判断:权限控制、表单验证、日志等级等
-
插件架构:支付方式、导出格式等
-
动态行为切换:主题切换、布局切换、组件渲染等
七、策略模式的进阶技巧
技巧 | 示例 |
---|---|
支持注册/注销策略 | StrategyMap.register('new', fn) |
结合工厂模式 | 动态生成策略对象 |
封装为类或插件 | 统一维护,提升复用性 |
与配置驱动结合 | 使用配置文件/后端数据决定策略行为 |
八、与其他设计模式的区别
模式 | 相同点 | 区别 |
---|---|---|
工厂模式 | 都封装行为创建 | 工厂关注对象创建,策略关注逻辑执行 |
状态模式 | 都有行为切换 | 状态模式强调状态变化,策略是行为选择 |
命令模式 | 都可执行操作 | 命令模式可撤销/排队,策略更偏向选择 |
✅ 九、总结
策略模式具有以下优点:
-
✅ 解耦逻辑,清晰可读
-
✅ 易于扩展,符合开闭原则
-
✅ 易于测试,策略函数天然可单测
-
✅ 非常适合动态渲染与业务逻辑分发
十、推荐封装方式(TypeScript 示例)
ts
type StrategyFn = (args?: any) => any;
class StrategyManager {
private strategies: Record<string, StrategyFn> = {};
register(key: string, fn: StrategyFn) {
this.strategies[key] = fn;
}
execute(key: string, args?: any) {
const fn = this.strategies[key];
if (!fn) throw new Error(`No strategy for key: ${key}`);
return fn(args);
}
}