策略模式
用策略模式消除 if-else,实现算法的灵活切换
问题
什么是策略模式?如何在前端中应用策略模式?
解答
策略模式将一系列算法封装成独立的策略对象,使它们可以相互替换,让算法的变化独立于使用它的客户端。
基本结构
// 策略对象:封装具体算法
const strategies = {
strategyA(params) {
// 算法 A
},
strategyB(params) {
// 算法 B
}
};
// 上下文:使用策略
function context(strategyName, params) {
return strategies[strategyName](params);
}
示例:表单验证
// 验证策略
const validators = {
required(value, message) {
return value.trim() !== '' ? null : message;
},
minLength(value, message, min) {
return value.length >= min ? null : message;
},
maxLength(value, message, max) {
return value.length <= max ? null : message;
},
email(value, message) {
const reg = /^[\w.-]+@[\w.-]+\.\w+$/;
return reg.test(value) ? null : message;
},
phone(value, message) {
const reg = /^1[3-9]\d{9}$/;
return reg.test(value) ? null : message;
}
};
// 验证器
class Validator {
constructor() {
this.rules = [];
}
// 添加规则
add(value, rules) {
rules.forEach(rule => {
this.rules.push(() => {
const { strategy, message, ...params } = rule;
const args = [value, message, ...Object.values(params)];
return validators[strategy](...args);
});
});
}
// 执行验证
validate() {
for (const rule of this.rules) {
const error = rule();
if (error) return error;
}
return null;
}
}
// 使用
const validator = new Validator();
validator.add('', [
{ strategy: 'required', message: '用户名不能为空' },
{ strategy: 'minLength', message: '用户名至少 3 个字符', min: 3 }
]);
validator.add('test@', [
{ strategy: 'email', message: '邮箱格式不正确' }
]);
const error = validator.validate();
console.log(error); // "用户名不能为空"
示例:价格计算
// 价格策略
const priceStrategies = {
normal(price) {
return price;
},
member(price) {
return price * 0.9; // 9 折
},
vip(price) {
return price * 0.8; // 8 折
},
promotion(price, discount) {
return price * discount;
}
};
// 计算价格
function calculatePrice(type, price, ...args) {
const strategy = priceStrategies[type];
if (!strategy) {
throw new Error(`未知的价格策略: ${type}`);
}
return strategy(price, ...args);
}
// 使用
console.log(calculatePrice('normal', 100)); // 100
console.log(calculatePrice('member', 100)); // 90
console.log(calculatePrice('vip', 100)); // 80
console.log(calculatePrice('promotion', 100, 0.7)); // 70
示例:动画缓动
// 缓动策略
const easings = {
linear(t) {
return t;
},
easeIn(t) {
return t * t;
},
easeOut(t) {
return t * (2 - t);
},
easeInOut(t) {
return t < 0.5
? 2 * t * t
: -1 + (4 - 2 * t) * t;
}
};
// 动画函数
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 easedProgress = easingFn(progress);
const value = from + (to - from) * easedProgress;
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
function calculatePrice(type, price) {
if (type === 'normal') {
return price;
} else if (type === 'member') {
return price * 0.9;
} else if (type === 'vip') {
return price * 0.8;
}
// 新增类型需要修改这个函数
}
// 用策略模式 - 开放扩展,关闭修改
const priceStrategies = {
normal: price => price,
member: price => price * 0.9,
vip: price => price * 0.8
};
// 新增策略只需添加属性
priceStrategies.superVip = price => price * 0.7;
关键点
- 策略模式将算法封装成独立对象,通过组合替代继承
- 消除条件分支,用策略对象的多态性替代 if-else
- 符合开闭原则:新增策略无需修改现有代码
- 前端常见场景:表单验证、价格计算、动画缓动、权限控制
- JavaScript 中函数是一等公民,策略可以直接用函数实现,无需类
目录