【设计模式】【前端】 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 代码很长,或者需要在运行时动态切换策略,就应该用策略模式! 🎯

相关推荐
To_OC1 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic1 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy1 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志2 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble3 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯4 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜4 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司5 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
江华森5 小时前
Web 开发基础:HTTP 与 REST
前端·网络协议·http
IT_陈寒6 小时前
Redis的KEYS命令把我搞崩溃了,改用SCAN才活过来
前端·人工智能·后端