Strategy Pattern
策略模式的实现与应用场景
问题
什么是策略模式?如何在 JavaScript 中实现策略模式?
解答
策略模式将一组算法封装成独立的策略对象,使它们可以相互替换,让算法的变化独立于使用它的客户端。
基本实现
// 定义策略对象
const strategies = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => a / b,
};
// 上下文:使用策略
function calculate(strategy, a, b) {
return strategies[strategy](a, b);
}
// 使用
console.log(calculate('add', 10, 5)); // 15
console.log(calculate('multiply', 10, 5)); // 50
表单验证示例
// 验证策略
const validators = {
required: (value) => ({
valid: value !== '',
message: '此字段必填',
}),
minLength: (min) => (value) => ({
valid: value.length >= min,
message: `最少 ${min} 个字符`,
}),
email: (value) => ({
valid: /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(value),
message: '邮箱格式不正确',
}),
phone: (value) => ({
valid: /^1[3-9]\d{9}$/.test(value),
message: '手机号格式不正确',
}),
};
// 验证器
class Validator {
constructor() {
this.rules = [];
}
// 添加验证规则
addRule(validator) {
this.rules.push(validator);
return this; // 支持链式调用
}
// 执行验证
validate(value) {
for (const rule of this.rules) {
const result = rule(value);
if (!result.valid) {
return result;
}
}
return { valid: true, message: '' };
}
}
// 使用
const emailValidator = new Validator()
.addRule(validators.required)
.addRule(validators.email);
console.log(emailValidator.validate('')); // { valid: false, message: '此字段必填' }
console.log(emailValidator.validate('abc')); // { valid: false, message: '邮箱格式不正确' }
console.log(emailValidator.validate('a@b.com')); // { valid: true, message: '' }
价格计算示例
// 折扣策略
const discountStrategies = {
normal: (price) => price,
vip: (price) => price * 0.9,
svip: (price) => price * 0.8,
coupon: (discount) => (price) => price - discount,
percent: (percent) => (price) => price * (1 - percent / 100),
};
// 价格计算器
class PriceCalculator {
constructor(strategy = discountStrategies.normal) {
this.strategy = strategy;
}
setStrategy(strategy) {
this.strategy = strategy;
}
calculate(price) {
return this.strategy(price);
}
}
// 使用
const calculator = new PriceCalculator();
// 普通用户
console.log(calculator.calculate(100)); // 100
// VIP 用户
calculator.setStrategy(discountStrategies.vip);
console.log(calculator.calculate(100)); // 90
// 使用优惠券
calculator.setStrategy(discountStrategies.coupon(15));
console.log(calculator.calculate(100)); // 85
动画缓动函数
// 缓动策略
const easings = {
linear: (t) => t,
easeIn: (t) => t * t,
easeOut: (t) => t * (2 - t),
easeInOut: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
bounce: (t) => {
if (t < 1 / 2.75) return 7.5625 * t * t;
if (t < 2 / 2.75) return 7.5625 * (t -= 1.5 / 2.75) * t + 0.75;
if (t < 2.5 / 2.75) return 7.5625 * (t -= 2.25 / 2.75) * t + 0.9375;
return 7.5625 * (t -= 2.625 / 2.75) * t + 0.984375;
},
};
// 动画函数
function animate({ from, to, duration, easing = 'linear', onUpdate }) {
const start = performance.now();
const easingFn = easings[easing];
function tick(now) {
const elapsed = now - start;
const progress = Math.min(elapsed / duration, 1);
const value = from + (to - from) * easingFn(progress);
onUpdate(value);
if (progress < 1) {
requestAnimationFrame(tick);
}
}
requestAnimationFrame(tick);
}
// 使用
animate({
from: 0,
to: 300,
duration: 1000,
easing: 'easeOut',
onUpdate: (value) => {
element.style.transform = `translateX(${value}px)`;
},
});
关键点
- 消除条件分支:用策略对象替代 if-else 或 switch-case
- 开闭原则:新增策略无需修改现有代码,只需添加新策略
- 策略可复用:同一策略可在不同上下文中使用
- 运行时切换:可以动态更换算法,无需修改客户端代码
- 适用场景:表单验证、价格计算、动画缓动、权限控制等多算法选择场景
目录