从表单校验到订单流转,彻底搞懂前端开发中的策略模式与状态模式

策略模式和状态模式是前端开发中最容易混淆的两个设计模式。它们的实现方式高度相似------都用对象映射替代条件分支,都通过委托来分发行为。但策略模式像你拿着遥控器手动换台,状态模式像空调根据室温自动调温。本质上,一个是外部选择行为,一个是内部自动切换。

本文从表单校验和订单状态流转两个真实场景出发,分别用策略模式和状态模式重构代码,深度对比两者的本质区别。

一、表单校验------策略模式的用武之地

在一个后台管理系统中,表单字段需要支持多种校验规则:必填、邮箱格式、手机号、最小长度等。最初的代码往往是这样的:

javascript 复制代码
function validate(value, type) {
  if (type === 'required') {
    return value && value.trim() ? '' : '必填';
  } else if (type === 'email') {
    return /^\S+@\S+\.\S+$/.test(value) ? '' : '邮箱格式错误';
  } else if (type === 'phone') {
    return /^1[3-9]\d{9}$/.test(value) ? '' : '手机号格式错误';
  } else if (type === 'minLength') {
    return value.length >= 6 ? '' : '至少6位';
  }
  return '';
}

这段代码的问题很明显:新增一种校验规则(比如身份证号),就要改动这个函数。随着业务发展,这个函数会越来越长,每次改动都可能影响已有逻辑。

策略模式的核心思路:把每种校验规则封装成独立的纯函数,用一个配置对象统一管理。

javascript 复制代码
// 每种校验规则是一个独立的策略函数
const validators = {
  required: (val) => (val && val.trim() ? '' : '必填'),
  email: (val) => (/^\S+@\S+\.\S+$/.test(val) ? '' : '邮箱格式错误'),
  phone: (val) => (/^1[3-9]\d{9}$/.test(val) ? '' : '手机号格式错误'),
  minLength: (val) => (val.length >= 6 ? '' : '至少6位'),
};

// 通用校验器:根据规则名选择对应的策略执行
function validate(value, rules) {
  for (const rule of rules) {
    const fn = typeof rule === 'function' ? rule : validators[rule];
    const error = fn(value);
    if (error) return error;
  }
  return '';
}

// 使用:调用方主动选择校验规则
validate('hello@test.com', ['required', 'email']);  // ''
validate('hi', ['required', 'email']);               // '邮箱格式错误'

注意关键特征 :调用方通过传入规则名('required''email'),主动选择 用哪个校验函数。这些校验函数之间互不依赖、完全平行------required 不需要知道 email 的存在,也不需要知道当前是在校验哪个表单。

这就是策略模式的核心价值:用配置对象替代条件分支,调用方主动选择算法,各算法互相独立,对扩展开放。

二、订单状态流转------状态模式的用武之地

再看另一个场景。电商系统中,订单从创建到完成会经历多个状态:待支付 → 已支付 → 已发货 → 已完成。每个状态下,同一个操作(如"支付")的行为完全不同------待支付时可以支付,已支付时应该提示"请勿重复支付",已发货时根本不应该有支付按钮。

最直接的写法是在每个操作方法里用 if-else 判断当前状态:

javascript 复制代码
class Order {
  constructor() {
    this.status = 'pending';
  }

  pay() {
    if (this.status === 'pending') {
      console.log('支付成功,更新为已支付');
      this.status = 'paid';
    } else if (this.status === 'paid') {
      console.log('订单已支付,请勿重复支付');
    } else if (this.status === 'shipped') {
      console.log('已发货订单不能支付');
    }
  }

  ship() {
    if (this.status === 'paid') {
      console.log('已发货');
      this.status = 'shipped';
    } else if (this.status === 'pending') {
      console.log('待支付订单不能发货');
    }
  }
}

状态一多,每个方法里都塞满了 if-else,新增一个状态(如"退款中")就要改所有相关方法。

状态模式的核心思路:将每种状态封装成独立的状态类,每个状态类内部定义了该状态下的所有行为。订单对象只负责调用当前状态的方法,行为自动根据状态变化。

javascript 复制代码
// 每个状态封装成独立的类
class PendingState {
  pay(order) {
    console.log('支付成功,更新为已支付');
    order.setState(new PaidState());  // 状态自己决定下一步转换到哪
  }
  ship() { throw new Error('待支付订单不能发货'); }
  complete() { throw new Error('待支付订单不能完成'); }
}

class PaidState {
  pay() { throw new Error('订单已支付,请勿重复支付'); }
  ship(order) {
    console.log('已发货');
    order.setState(new ShippedState());
  }
  complete() { throw new Error('请先发货'); }
}

class ShippedState {
  pay() { throw new Error('已发货订单不能支付'); }
  ship() { throw new Error('已发货订单不能重复发货'); }
  complete(order) {
    console.log('订单已完成');
    order.setState(new CompletedState());
  }
}

class CompletedState {
  pay() { throw new Error('已完成订单不能支付'); }
  ship() { throw new Error('已完成订单不能发货'); }
  complete() { throw new Error('订单已完成'); }
}

// 订单类:只负责委托给当前状态
class Order {
  constructor() {
    this.state = new PendingState();
  }
  setState(state) { this.state = state; }
  pay() { this.state.pay(this); }      // 行为由当前状态决定
  ship() { this.state.ship(this); }
  complete() { this.state.complete(this); }
}

注意关键特征 :调用方只调用 order.pay()不需要知道订单当前是什么状态 。订单对象内部自己根据状态决定行为------待支付时执行支付逻辑,已支付时抛出错误。状态的转换也由状态类自己管理(PendingState.pay 内部调用 order.setState(new PaidState()))。

这就是状态模式的核心价值:对象根据内部状态自动改变行为,状态转换被封装在各个状态类中,调用方无需感知当前状态。

三、核心区别:"谁在选择"

回到最初的问题:策略模式和状态模式到底有什么区别?它们都用对象映射消灭了 if-else,代码结构甚至看起来差不多。

最根本的区别在于"选择权在谁手中"

对比维度 策略模式 状态模式
选择权 调用方主动选择用哪个策略 对象内部根据状态自动决定
调用方式 validators['email'](value) ------ 外部指定算法名 order.pay() ------ 外部不知道内部状态
函数间关系 各策略函数互不依赖,完全平行 状态行为可能相互调用,存在层级关系
典型场景 表单校验、价格计算、排序算法 订单流转、登录态、播放器状态
新增扩展 加一个策略函数 加一个状态类

一个最直观的判断方法

  • 如果你发现调用方在说"用这个算法来处理这个数据",那是策略模式------外部主动选择。
  • 如果你发现调用方在说"做这件事",然后对象自己决定怎么做,那是状态模式------内部自动变化。

四、总结

特征 策略模式 状态模式
谁在选 调用方选 对象自己变
函数间关系 平行独立 可能互相调用
调用方是否感知当前状态 是(需要传入 tag) 否(只调用方法)
典型场景 表单校验、价格计算 订单流转、登录态
一句话 "我给你选项,你选一个" "我根据自身状态,自己决定怎么做"

下次当你面对一堆 if-else 时,先问自己一个问题:"是调用方在主动选择不同算法,还是对象自己在不同状态下表现出不同行为?" 答案会直接告诉你该用策略模式还是状态模式。

相关推荐
月弦笙音1 小时前
# SSE 连接可靠性方案
前端
qq_589666051 小时前
npm install npm install -g @vue/cli 分别是什么意思以及安装路径
前端·vue.js·npm
用户0207199207722 小时前
Kafka 与事务 Outbox
前端
IT_陈寒2 小时前
Vite的静态资源路径搞得我头秃,原来这样配才对
前端·人工智能·后端
帅次2 小时前
Android 高级工程师面试:Flutter 路由导航 近1年高频追问 18 题
android·flutter·面试
BerryS3N2 小时前
前端应用的离线暂停更新策略:构建稳定可靠的渐进式部署方案
前端
zzz_23682 小时前
【Java实习面试算法冲刺】总复盘
java·算法·面试
LaughingZhu3 小时前
Product Hunt 每日热榜 | 2026-07-17
前端·数据库·人工智能·经验分享·mysql·chatgpt·html
潇凝子潇3 小时前
UnsupportedOperationException
java·前端·数据库