前端 JavaScript 如何实现策略模式?
在前端开发中,策略模式常用于:
- 表单验证(不同输入字段不同校验规则)
- 排序算法(支持多种排序方式)
- 支付方式切换(微信支付、支付宝、银行卡)
- 动画策略(不同的动画效果)
一、策略模式的 JavaScript 实现
1. 传统写法(多方法模式,不符合策略模式)
如果不使用策略模式,可能会像这样写:
javascript
class Calculator {
add(a, b) {
return a + b;
}
subtract(a, b) {
return a - b;
}
multiply(a, b) {
return a * b;
}
}
const calc = new Calculator();
console.log(calc.add(10, 5)); // 15
console.log(calc.subtract(10, 5)); // 5
console.log(calc.multiply(10, 5)); // 50
问题:
- 不符合"开闭原则" :如果要新增除法方法,需要修改
Calculator
类。 - 难以动态切换策略:必须手动调用不同的方法,而不是动态选择。
2. 使用策略模式(符合 OCP 原则)
我们用对象字面量 + 高阶函数来实现策略模式:
(1) 定义策略对象
javascript
const strategies = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b
};
(2) 定义 Context
上下文
javascript
class Context {
constructor(strategy) {
this.strategy = strategy;
}
setStrategy(strategy) {
this.strategy = strategy; // 允许动态更换策略
}
executeStrategy(a, b) {
return this.strategy(a, b);
}
}
(3) 运行时动态切换策略
javascript
const context = new Context(strategies.add); // 设定初始策略为加法
console.log("10 + 5 =", context.executeStrategy(10, 5)); // 15
context.setStrategy(strategies.subtract); // 切换为减法
console.log("10 - 5 =", context.executeStrategy(10, 5)); // 5
context.setStrategy(strategies.multiply); // 切换为乘法
console.log("10 * 5 =", context.executeStrategy(10, 5)); // 50
二、前端实际应用案例
1. 表单验证策略
在前端开发中,我们常需要对不同的输入字段执行不同的验证规则,例如:
- 邮箱格式 :必须包含
@
- 手机号格式:必须是 11 位数字
- 密码长度:至少 6 位
(1) 定义验证策略
javascript
const validationStrategies = {
isNonEmpty: (value) => value.trim() !== "" || "不能为空",
isEmail: (value) => /\S+@\S+\.\S+/.test(value) || "邮箱格式不正确",
isPhone: (value) => /^\d{11}$/.test(value) || "手机号格式不正确",
minLength: (min) => (value) => value.length >= min || `长度不能少于 ${min} 位`
};
(2) 验证函数
javascript
function validate(value, rules) {
for (const rule of rules) {
const result = rule(value);
if (result !== true) {
return result; // 返回第一个错误信息
}
}
return true;
}
(3) 使用策略进行表单验证
javascript
const emailInput = "[email protected]";
const phoneInput = "13800138000";
const passwordInput = "12345";
console.log(validate(emailInput, [validationStrategies.isNonEmpty, validationStrategies.isEmail]));
// 输出: true
console.log(validate(phoneInput, [validationStrategies.isNonEmpty, validationStrategies.isPhone]));
// 输出: true
console.log(validate(passwordInput, [validationStrategies.isNonEmpty, validationStrategies.minLength(6)]));
// 输出: "长度不能少于 6 位"
好处:
- 新增校验规则时,不需要修改
validate
函数,只需扩展validationStrategies
。
2. 购物车优惠策略
电商系统通常会有不同的折扣策略:
- 满 100 减 10
- VIP 会员打 9 折
- 满 200 送 20 元代金券
(1) 定义折扣策略
javascript
const discountStrategies = {
noDiscount: (price) => price,
tenOffIfOver100: (price) => price >= 100 ? price - 10 : price,
vipDiscount: (price) => price * 0.9,
couponFor200: (price) => price >= 200 ? price - 20 : price
};
(2) 定义上下文
javascript
class Cart {
constructor(strategy) {
this.strategy = strategy;
}
setStrategy(strategy) {
this.strategy = strategy;
}
calculateTotal(price) {
return this.strategy(price);
}
}
(3) 使用折扣策略
javascript
const cart = new Cart(discountStrategies.noDiscount);
console.log("原价 150,折扣后 =", cart.calculateTotal(150)); // 150
cart.setStrategy(discountStrategies.tenOffIfOver100);
console.log("原价 150,折扣后 =", cart.calculateTotal(150)); // 140
cart.setStrategy(discountStrategies.vipDiscount);
console.log("原价 150,VIP 价格 =", cart.calculateTotal(150)); // 135
好处:
- 支持多种折扣策略,动态切换
- 符合开闭原则,扩展新策略时无需修改
Cart
代码
三、策略模式在前端的适用场景
场景 | 示例 |
---|---|
表单验证 | 根据输入类型选择不同的验证规则 |
支付方式 | 切换支付宝、微信、银行卡支付 |
动画策略 | 切换淡入淡出、滑动动画等不同的实现 |
排序方法 | 提供不同的排序策略(冒泡、快速、插入排序) |
数据格式化 | 不同地区的时间、货币格式化 |
四、总结
✅ 策略模式适用于多个可替换算法的场景,并且能在运行时动态切换策略。
✅ 在前端,常用于表单验证、支付方式、购物车折扣等场景。
✅ 用对象字面量 + 高阶函数可以更简洁地实现策略模式。
✅ 符合 SOLID 原则,尤其是"开闭原则(OCP)",让代码更易扩展。
🚀 在前端开发中,如果你发现
if-else
语句过多,并且算法可能会变化,就可以考虑策略模式!
策略模式 vs. switch
语句
在前端开发中,很多人会用 switch
语句来处理不同的逻辑,比如:
- 处理不同的 支付方式 (
switch(paymentType)
) - 处理不同的 用户角色 (
switch(userRole)
) - 处理不同的 排序方式 (
switch(sortType)
)
但 switch
有一些 缺点 ,而 策略模式(Strategy Pattern) 可以优化代码,让代码更清晰、更易扩展。
一、使用 switch
的实现
示例:计算器
我们先用 switch
语句来实现一个简单的计算器:
javascript
function calculate(operation, a, b) {
switch (operation) {
case "add":
return a + b;
case "subtract":
return a - b;
case "multiply":
return a * b;
case "divide":
return b !== 0 ? a / b : "Cannot divide by zero";
default:
return "Invalid operation";
}
}
console.log(calculate("add", 10, 5)); // 15
console.log(calculate("subtract", 10, 5)); // 5
console.log(calculate("multiply", 10, 5)); // 50
console.log(calculate("divide", 10, 5)); // 2
switch
的缺点
❌ 违背开闭原则(OCP):
- 每次新增运算方式(如
modulus
取余),必须修改calculate
函数。 - 高耦合度 :所有逻辑都写在
calculate
里,代码难以维护。
❌ 难以动态扩展:
- 不能在运行时轻松切换策略。
- 代码可读性变差,
switch
语句可能变得很长。
二、使用策略模式优化
策略对象
javascript
const strategies = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => (b !== 0 ? a / b : "Cannot divide by zero"),
};
策略上下文
javascript
class Context {
constructor(strategy) {
this.strategy = strategy;
}
setStrategy(strategy) {
this.strategy = strategy; // 允许动态切换策略
}
execute(a, b) {
return this.strategy(a, b);
}
}
运行时动态切换
javascript
const context = new Context(strategies.add);
console.log(context.execute(10, 5)); // 15
context.setStrategy(strategies.subtract);
console.log(context.execute(10, 5)); // 5
context.setStrategy(strategies.multiply);
console.log(context.execute(10, 5)); // 50
三、switch
vs. 策略模式对比
对比项 | switch 语句 |
策略模式 |
---|---|---|
开闭原则(OCP) | 需要修改原代码,违背 OCP | 新增策略时不改动原代码,符合 OCP |
代码结构 | 逻辑集中在一个函数,耦合度高 | 策略独立,结构清晰 |
可扩展性 | 难以扩展,每新增一个策略,都要改 switch 代码 |
只需新增一个策略对象,完全不影响现有代码 |
可读性 | switch 可能变得很长,可读性下降 |
策略模式让代码更清晰,避免 if-else 或 switch |
动态切换策略 | 不能在运行时更改,只能靠 if-else 或 switch |
可以在运行时灵活切换策略 |
四、策略模式的适用场景
✅ 适合的场景
- 代码中包含多个可替换的算法(如支付方式、验证规则)。
- 需要动态切换不同的策略,而不是用
if-else
或switch
硬编码。 - 代码需要符合 开闭原则(OCP),避免频繁修改核心逻辑。
❌ 不适合的场景
- 如果策略数量很少(只有 2-3 个),使用
switch
或if-else
更简单。 - 如果策略不会发生变化,不需要动态扩展。
五、总结
🚀 什么时候用 switch
?
- 逻辑简单,策略固定,策略数量少时(如 2-3 个)。
🚀 什么时候用策略模式?
- 策略数量多 ,可能动态扩展(如表单校验、支付方式、动画效果)。
- 希望代码清晰、可读性高 ,避免
switch
语句过长。 - 希望符合 SOLID 原则 ,特别是开闭原则(OCP)。
一句话总结 :
如果switch
代码很长,或者需要在运行时动态切换策略,就应该用策略模式! 🎯