恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

设计模式 18 · 状态模式

  • 首页
  • 资讯中心
  • /
  • 设计模式 18 · 状态模式

相关资讯

Mysql中慢 SQL 优化实战:减少不必要的 JOIN 关联 2026/8/11 0:32:27
AI记忆管理与企业知识库:巴别鸟感忆定位与智巢AI的演进路径 2026/8/11 0:32:27
为什么 RS-485 选型该多看国科安芯 ASM485S:从技术原理讲透 2026/8/11 0:32:27

最新资讯

从Vibe Coding到SDD:AI时代规范驱动开发实践指南
Nginx 从入门到实践:部署、配置、反向代理与负载均衡全解
小滴课堂资源分享工业级PaaS云平台+SpringCloudAlibaba综合项目课程
模型服务部署开发短记:GPU 配置怎样核验
Claude Code 套餐路由的漏洞:免费用户竟能调用我的 GPT-4 配额——三层网关改造实录
Flutter+OpenHarmony构建高校资产管理系统实践

今日推荐

《人工智能导论:深度学习大模型基础》全套PPT课件2026
9.5 技术债务的重构:何时该动一次大手术
如何用Video2X实现专业级视频画质提升:AI视频增强完整指南

本周热门

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁
如何快速生成中国车牌图片:Python开源工具完整指南
当 LLM 遇见大文档:主流开源项目如何处理上下文超限

本月精选

如何用DamaiHelper实现演唱会门票的智能自动化抢购:完整技术解决方案指南
第4篇:59 倍性能差距的索引瓶颈定位——一次教科书级的全表扫描调优
终极歌词批量下载神器:5分钟解决离线音乐库歌词同步难题

设计模式 18 · 状态模式

发布时间:2026/8/11 0:37:27
设计模式 18 · 状态模式 前面我们埋过好几次钩子:策略那篇说策略是平行选一个,状态是链式流转,这一篇终于轮到状态模式(State)正式登场,把这条界限彻底讲透。它和策略模式的代码结构几乎一模一样(都是持有一个接口引用、委托调用),却是行为型里最经典的一对双胞胎——长得像,内核完全不同。状态模式解决的问题一句话:一个对象的行为,随它自身所处的状态而变化,而且状态之间会按规则相互流转。关键词是行为随状态变和状态会流转。生活里的例子随处可见:一部电梯,在运行中你按开门键没反应,在停靠中按了才开门——同一个动作(按开门),在不同状态下行为完全不同;而电梯的状态还会流转:停靠→运行→停靠。红绿灯也是:红灯亮着,时间到了自动变绿,绿变黄,黄变红,状态按固定规则一环扣一环地转。我们的订单场景里,有一个教科书级的状态机例子:订单的生命周期。一笔订单会经历待付款 → 已付款 → 已发货 → 已完成这一串状态,中间还可能取消。而订单的行为强烈依赖于它当前的状态:待付款的订单可以支付、可以取消;已发货的订单不能取消、只能确认收货;“已完成的订单什么操作都不能做。同样是取消这个动作,在不同状态下,有的允许、有的直接拒绝。如果用一堆if-else判断当前状态来决定行为,代码会变成一个又臭又长、状态一多就失控的泥潭。状态模式,就是把每个状态做成一个独立的类,让状态自己决定在我这个状态下,各个动作该怎么做、该流转到哪个状态”。这篇文章按这条线索展开:先看用状态字段 一堆 if-else 判断的困境;再引出状态模式如何把每个状态对象化;然后讲清它的角色、以及状态流转的两种驱动方式;接着重点辨析状态与策略这对双胞胎(这是本篇的核心);最后给出适用边界。贯穿例是订单状态机。目录用 if-else 判断状态的困境状态模式:把每个状态对象化角色,与状态流转谁来驱动状态 vs 策略:一对双胞胎的彻底辨析什么时候用状态模式一、用 if-else 判断状态的困境看订单的操作。订单有个当前状态字段,每个操作都要先判断当前状态、再决定怎么做:publicclassOrder{privateStringstatus;// 待付款、已付款、已发货、已完成、已取消// 支付操作publicvoidpay(){if(status.equals(待付款)){status已付款;// 只有待付款能支付}else{thrownewRuntimeException(当前状态不能支付);}}// 取消操作publicvoidcancel(){if(status.equals(待付款)){status已取消;// 待付款能取消}elseif(status.equals(已付款)){status已取消;// 已付款也能取消(要退款)}else{thrownewRuntimeException(当前状态不能取消);// 已发货/已完成不能取消}}// 发货操作publicvoidship(){if(status.equals(已付款)){status已发货;}else{thrownewRuntimeException(当前状态不能发货);}}// ... 每个操作都是一坨状态判断}这段代码能跑,但随着状态和操作变多,它会迅速失控:每个方法都是一坨 if-else:每个操作(pay/cancel/ship/confirm…)内部,都要把所有相关状态判断一遍。状态一多,每个方法都又臭又长。状态转移规则散落各处:待付款能干什么、能转到哪些状态这个规则,被打散在 pay、cancel、ship 各个方法里。你想搞清楚待付款状态的完整行为,得把所有方法翻一遍。违反开闭原则:新增一个状态(比如退款中),你得回到每一个操作方法里,都加一个else if分支。改一处,全都要动、全都要重测。容易出非法流转:全靠人肉判断,一不小心就可能让订单从已完成又跳回待付款这种非法状态,而编译器毫不知情。问题的根源是:状态只是一个字符串字段,而每个状态下的行为和转移规则被硬编码、打散在各个操作方法的 if-else 里。我们真正想要的是:把每一个状态都变成一个独立的对象,让这个状态对象自己封装在我这个状态下,各个操作该怎么响应、该流转到哪个状态。这样,待付款的所有规则都集中在待付款状态这一个类里,清清爽爽。这就是状态模式。二、状态模式:把每个状态对象化状态模式的做法:定义一个状态接口,声明所有操作;每个具体状态是一个类,实现在本状态下这些操作各自怎么做、流转到哪个状态;订单(上下文)持有一个当前状态对象,把操作委托给它,并允许状态切换。第一步,定义状态接口(声明所有操作):publicinterfaceOrderState{voidpay(OrderContextctx);voidcancel(OrderContextctx);voidship(OrderContextctx);// ... 所有操作}第二步,每个状态是一个类,封装本状态下的行为和转移:// 待付款状态publicclassPendingPayStateimplementsOrderState{publicvoidpay(OrderContextctx){System.out.println(支付成功);ctx.setState(newPaidState());// 流转到已付款}publicvoidcancel(OrderContextctx){System.out.println(订单已取消);ctx.setState(newCancelledState());// 流转到已取消}publicvoidship(OrderContextctx){thrownewRuntimeException(未付款不能发货);// 本状态不允许}}// 已付款状态publicclassPaidStateimplementsOrderState{publicvoidpay(OrderContextctx){thrownewRuntimeException(请勿重复支付);}publicvoidcancel(OrderContextctx){System.out.println(已付款订单取消,发起退款);ctx.setState(newCancelledState());}publicvoidship(OrderContextctx){System.out.println(已发货);ctx.setState(newShippedState());// 流转到已发货}}// ShippedState、CompletedState、CancelledState 同理...第三步,上下文(订单)持有当前状态,把操作委托出去:publicclassOrderContext{privateOrderStatestatenewPendingPayState();// 初始状态:待付款publicvoidsetState(OrderStatestate){this.statestate;}// 所有操作,都委托给当前状态对象去处理publicvoidpay(){state.pay(this);}publicvoidcancel(){state.cancel(this);}publicvoidship(){state.ship(this);}}用起来,订单的行为随状态自动变化,流转由状态自己驱动:OrderContextordernewOrderContext();// 待付款order.pay();// 支付成功 → 自动流转到已付款order.ship();// 已发货 → 自动流转到已发货order.cancel();// 抛异常:已发货不能取消对比第一节,升级点非常清晰:每个状态的所有规则(本状态下各操作怎么响应、转到哪)都集中在它自己的类里,一个if-else都没有;OrderContext只管把操作委托给当前状态;要加一个退款中状态,新增一个RefundingState类就行,已有状态类基本不用动。订单当前能干什么这件事,由当前是哪个状态对象来决定,而不是靠满地的状态判断。用一张图看这个状态机最清楚:图里最该记住的,是那张状态之间带箭头的流转图——它就是业务上的订单状态机。状态模式的精髓,就是把这张状态图,从散落的 if-else 里,变成一组各自独立的状态类 它们之间明确的流转关系。哪些流转是合法的,一目了然地写在各状态类里;非法的流转(比如已完成跳回待付款)压根没有对应代码,自然就杜绝了。三、角色,与状态流转谁来驱动状态模式的角色,三个:角色本例中是谁职责上下文(Context)OrderContext持有当前状态,把操作委托给它抽象状态(State)OrderState接口声明各状态共有的操作具体状态(ConcreteState)PendingPayState等封装本状态的行为 流转逻辑一个关键的设计问题:状态的流转(从一个状态切到下一个),该由谁来驱动?有两种做法:由状态对象自己驱动(我们上面用的):每个状态在处理操作时,自己决定处理完该转到哪个状态,调用ctx.setState(...)完成切换。好处是流转逻辑高度内聚——待付款之后去哪这个规则,就写在PendingPayState里。缺点是状态类之间产生了依赖(PendingPayState得知道PaidState的存在)。由上下文统一驱动:状态对象只处理行为、返回结果,由上下文根据结果决定切到哪个状态。好处是状态之间解耦,流转规则集中在一处。缺点是上下文又变重了一点。实际项目里两种都有。状态自己驱动更符合状态模式的原味(把状态机分散进各状态),适合流转规则相对固定的场景;上下文驱动适合流转规则复杂、需要集中管理的场景。对于订单这种流转清晰的状态机,状态自驱通常更自然。四、状态 vs 策略:一对双胞胎的彻底辨析这是本篇的重头戏。状态和策略,是设计模式里最像的一对——把它们的类图画出来,几乎完全一样:都有一个 Context 持有一个接口引用,都把行为委托给具体实现,都用多态替代了条件分支。很多人到这里就懵了:它们到底有什么区别?区别不在结构,而在意图和用法,有三个关键差异:差异一:各个实现之间有没有关系。策略的各个算法是完全平行、互相独立的。运费的包邮策略和按重策略之间,毫无关系,谁也不知道谁的存在。状态的各个状态是互相关联、会流转的。“待付款知道自己之后会变成已付款”,“已付款知道自己会变成已发货”——状态之间有明确的转移关系,构成一张状态图。差异二:谁来决定用哪个实现。策略:由外部(客户端)决定用哪个策略,主动setStrategy(new WeightShipping())塞进去。策略自己不会切换自己。状态:通常由状态自己(或上下文)根据业务规则自动流转到下一个状态。你不会从外面设置订单是什么状态,而是订单在操作过程中自己转过去。差异三:切换的频率和意图。策略:一旦选定,通常在整个过程中不变(这一单就用按重计费)。切换是换一种做法。状态:在对象生命周期里不断流转(订单从待付款一路变到完成)。切换是进入了下一个阶段。用一句话彻底记住:策略是横向地、平行地选一个算法(选择),状态是纵向地、按规则流转的一串阶段(流转)。或者更形象:策略像是点菜——菜单上的菜互不相干,你挑一个;状态像是闯关——一关通了自动进下一关,关卡之间有顺序。结构骗人,意图不骗人。还有一个实用的判断技巧:看有没有状态转移图。如果你能为这些实现画出一张从 A 状态到 B 状态的箭头图,那就是状态模式;如果这些实现之间画不出任何箭头、纯粹是平级的选项,那就是策略模式。订单能画出状态机图 → 状态;运费的几种算法画不出流转 → 策略。五、什么时候用状态模式适合用状态模式的信号:一个对象的行为明显依赖于它的状态,不同状态下同一操作的行为差异很大;存在明确的状态流转规则(能画出状态机图);代码里出现了大量根据状态字段做 if-else/switch的判断,且状态和操作都会增加。不必用的信号:状态就两三个、行为差异也不大——那简单的 if-else 或枚举更直白,套状态模式凭空多出一堆状态类,是过度设计;各状态之间没有流转关系,只是平级选项——那是策略,不是状态;状态机极其复杂(几十个状态、上百条流转规则)——那可能需要专门的状态机引擎/工作流框架(如 Spring StateMachine),手写状态类会难以维护。判断的核心还是那句话:先确认真的存在行为随状态变 状态按规则流转这个结构(能画出状态机图),状态模式才值得上。而且要和策略分清楚——别把一堆平行算法硬说成状态。小结。状态模式把一个对象的每个状态都变成一个独立的状态类,让状态自己封装在本状态下各操作怎么响应、该流转到哪个状态,上下文只持有当前状态对象并委托操作——从而把散落在各方法里的状态 if-else,变成一组清晰的状态类 明确的流转关系(状态机),新增状态不动或少动已有代码,还天然杜绝非法流转。它和策略是结构几乎相同的双胞胎,但策略是平行地选一个算法、由外部指定、通常不变;状态是按规则流转的一串阶段、自动切换、不断变化——能画出状态转移图的就是状态。订单生命周期是它最经典的应用。下一篇我们讲命令模式——它把一个请求/操作本身封装成对象,从而能把操作排队、记录、撤销和重做,典型如订单操作的撤销功能。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号