前端常用设计模式 - 策略模式

309 阅读3分钟

在开发中,我们经常会遇到一些场景,需要根据不同条件来处理逻辑。最常见的做法可能是用一连串的 if-elseswitch-case 来实现。虽然这种方式在简单场景下是有效的,但随着业务逻辑的复杂度增加,代码可能会变得难以维护。这时候,引入策略模式这种设计思路,往往能让代码更清晰、更具扩展性。


策略模式的核心思想

策略模式的核心在于“分离”。它将不同的行为逻辑(即策略)封装到独立的模块中,通过一个统一的接口进行调用。换句话说,你可以根据实际需要动态选择某个策略,而无需在主逻辑中堆砌复杂的判断。

比如,在一个电商系统中,不同类型的用户可能享有不同的折扣规则:普通用户不打折,会员九折,VIP八折。如果按照常规写法,代码可能是这样的:

function calculatePrice(userType, price) {
  if (userType === "normal") {
    return price;
  } else if (userType === "member") {
    return price * 0.9;
  } else if (userType === "vip") {
    return price * 0.8;
  }
}

这段代码虽然能工作,但如果将来新增“超级 VIP”或“限时活动”折扣,就需要频繁修改 calculatePrice,难以保持清晰和扩展性。

策略模式的实现

通过策略模式,我们可以将不同的折扣规则抽离成独立的函数,以实现灵活扩展。下面是一种简单的实现。

封装策略

首先,将各类折扣规则封装成独立的策略函数,并通过一个对象统一管理。这样,所有的规则逻辑都可以集中在一起。

const discountStrategies = {
  normal: (price) => price, // 普通用户:不打折
  member: (price) => price * 0.9, // 会员:九折
  vip: (price) => price * 0.8, // VIP:八折
};

这个结构清晰而且易于扩展。如果未来需要新增“超级 VIP”用户,只需增加一条策略:

discountStrategies.superVip = (price) => price * 0.7; // 超级 VIP:七折

动态选择策略

接下来,我们需要在业务逻辑中动态选择并应用适当的策略。可以通过用户类型从策略对象中取出对应的函数,避免复杂的条件判断:

function calculatePrice(userType, price) {
  const strategy = discountStrategies[userType];
  if (!strategy) {
    throw new Error("未知的用户类型");
  }
  return strategy(price);
}

在调用时,只需传入用户类型和商品价格即可:

const price = calculatePrice("vip", 100); // 输出:80

策略模式的优势

使用策略模式的好处显而易见。首先,它将具体的逻辑从主函数中抽离出来,降低了代码的耦合度。其次,它非常容易扩展,新增加的逻辑不会影响已有代码,从而符合开闭原则。最后,策略之间互不干扰,代码更加清晰且便于维护。

策略模式的价值不在于代码看起来有多高级,而在于它提供了一种结构化、可扩展的解决问题的方式。在我们日常开发中,当遇到需要动态切换逻辑、或者逻辑复杂度让人头疼时,不妨尝试用策略模式去重构代码。它或许不是唯一的解法,但在很多情况下,它能让代码变得更优雅。