策略模式

用 JavaScript 实现策略模式,消除条件分支

问题

什么是策略模式?如何用 JavaScript 实现?

解答

策略模式将一系列算法封装成独立的策略对象,使它们可以相互替换,避免大量的 if-else 或 switch-case。

场景:表单验证

不使用策略模式的写法:

function validate(value, type) {
  if (type === 'required') {
    return value !== '';
  } else if (type === 'email') {
    return /^[\w-]+@[\w-]+\.\w+$/.test(value);
  } else if (type === 'phone') {
    return /^1[3-9]\d{9}$/.test(value);
  }
  // 每增加一种验证,就要改这个函数
}

使用策略模式:

// 策略对象:每种验证规则是一个策略
const strategies = {
  required(value) {
    return value !== '' || '不能为空';
  },
  email(value) {
    return /^[\w-]+@[\w-]+\.\w+$/.test(value) || '邮箱格式错误';
  },
  phone(value) {
    return /^1[3-9]\d{9}$/.test(value) || '手机号格式错误';
  },
  minLength(value, length) {
    return value.length >= length || `最少 ${length} 个字符`;
  }
};

// 验证器:使用策略
class Validator {
  constructor() {
    this.rules = [];
  }

  // 添加验证规则
  add(value, rule) {
    const [strategyName, ...params] = rule.split(':');
    this.rules.push(() => strategies[strategyName](value, ...params));
  }

  // 执行验证
  validate() {
    for (const rule of this.rules) {
      const result = rule();
      if (result !== true) {
        return result; // 返回错误信息
      }
    }
    return true;
  }
}

// 使用
const validator = new Validator();
validator.add('', 'required');
validator.add('test@example.com', 'email');
validator.add('abc', 'minLength:6');

const result = validator.validate();
console.log(result); // "不能为空"

场景:价格计算

// 策略对象:不同会员等级的折扣策略
const priceStrategies = {
  normal: (price) => price,
  vip: (price) => price * 0.9,
  svip: (price) => price * 0.8,
  blackCard: (price) => price * 0.7
};

// 计算价格
function calculatePrice(price, level) {
  return priceStrategies[level](price);
}

console.log(calculatePrice(100, 'vip'));  // 90
console.log(calculatePrice(100, 'svip')); // 80

场景:动画缓动函数

// 缓动策略
const easingStrategies = {
  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)
};

function animate(duration, easing, callback) {
  const start = performance.now();
  const easingFn = easingStrategies[easing];

  function tick(now) {
    const progress = Math.min((now - start) / duration, 1);
    callback(easingFn(progress));
    if (progress < 1) {
      requestAnimationFrame(tick);
    }
  }

  requestAnimationFrame(tick);
}

// 使用
animate(1000, 'easeOut', (value) => {
  element.style.opacity = value;
});

关键点

  • 策略模式把算法封装成对象,通过组合替代继承
  • 消除 if-else/switch-case,新增策略无需修改原有代码
  • JavaScript 中函数是一等公民,策略可以直接用函数表示
  • 适用场景:表单验证、价格计算、动画缓动、权限判断等
  • 符合开闭原则:对扩展开放,对修改关闭