【设计模式】【前端】 JavaScript 如何实现策略模式

前端 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 = "test@example.com";
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-elseswitch
动态切换策略 不能在运行时更改,只能靠 if-elseswitch 可以在运行时灵活切换策略

四、策略模式的适用场景

适合的场景

  • 代码中包含多个可替换的算法(如支付方式、验证规则)。
  • 需要动态切换不同的策略,而不是用 if-elseswitch 硬编码。
  • 代码需要符合 开闭原则(OCP),避免频繁修改核心逻辑。

不适合的场景

  • 如果策略数量很少(只有 2-3 个),使用 switchif-else 更简单。
  • 如果策略不会发生变化,不需要动态扩展。

五、总结

🚀 什么时候用 switch

  • 逻辑简单,策略固定,策略数量少时(如 2-3 个)。

🚀 什么时候用策略模式?

  • 策略数量多 ,可能动态扩展(如表单校验、支付方式、动画效果)。
  • 希望代码清晰、可读性高 ,避免 switch 语句过长。
  • 希望符合 SOLID 原则 ,特别是开闭原则(OCP)

一句话总结
如果 switch 代码很长,或者需要在运行时动态切换策略,就应该用策略模式! 🎯

相关推荐
火柴就是我2 分钟前
每日见闻之Three.js 根据官方demo 理解相机位置
前端
KarrySmile3 分钟前
Day04–链表–24. 两两交换链表中的节点,19. 删除链表的倒数第 N 个结点,面试题 02.07. 链表相交,142. 环形链表 II
算法·链表·面试·双指针法·虚拟头结点·环形链表
JosieBook11 分钟前
【web应用】基于Vue3和Spring Boot的课程管理前后端数据交互过程
前端·spring boot·交互
刘大猫.18 分钟前
npm ERR! cb() never called!
前端·npm·node.js·npm install·npmm err·never called
咔咔一顿操作22 分钟前
常见问题三
前端·javascript·vue.js·前端框架
上单带刀不带妹24 分钟前
Web Worker:解锁浏览器多线程,提升前端性能与体验
前端·js·web worke
电商API大数据接口开发Cris40 分钟前
Node.js + TypeScript 开发健壮的淘宝商品 API SDK
前端·数据挖掘·api
还要啥名字42 分钟前
基于elpis下 DSL有感
前端
一只毛驴1 小时前
谈谈浏览器的DOM事件-从0级到2级
前端·面试
用户8168694747251 小时前
封装ajax
前端