[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"site-settings":3,"\u002Farticles\u002Fdesign-pattern-19":26,"\u002Farticles\u002Fdesign-pattern-19\u002Frelated":43,"\u002Farticles\u002Fdesign-pattern-19\u002Fadjacent":72,"\u002Fcategories":83,"render-design-pattern-19":109},{"code":4,"message":5,"data":6},0,"ok",{"author_city":7,"author_years":8,"site_tagline":9,"site_description":10,"skill_one_desc":11,"author_status":12,"skill_two_title":13,"about_hero_image":9,"skill_one_title":14,"icp":15,"author_role":16,"og_default_image":9,"closing_quote_date":17,"toolkit":18,"skill_two_desc":19,"author_bio":9,"site_footer":9,"site_url":20,"author_tagline":21,"weekly_thought":9,"tagline_line_one":22,"author_bio_two":9,"closing_quote":23,"site_name":24,"tagline_line_two":25,"home_hero_image":9},"西安","10","","花开空白 - 个人博客","熟悉Java、Python、Go等后端语言。","努力工作中","前端开发","后端开发","陕ICP备2021009682号-1","开发者","2022年·秋","Java,JavaScript,TypeScript,Python,Go","熟悉Js、Ts语言以及Vue、uniapp等框架。","https:\u002F\u002Firule.cn","很高兴遇见你。","把复杂的事，","把每次一发布，都当成一次和陌生人的握手。","花开空白","写成简单的故事。",{"code":4,"message":5,"data":27},{"id":28,"slug":29,"title":30,"summary":31,"coverUrl":9,"categoryId":32,"status":33,"topFlag":4,"viewCount":34,"likeCount":4,"wordCount":35,"readingMinutes":36,"publishedAt":37,"contentMd":38,"contentHtml":39,"tags":40,"metaTitle":9,"metaDescription":9,"keywords":9,"canonicalUrl":9,"robots":41,"ogImage":9,"faq":42,"aiSummary":42},"2113933842262790145","design-pattern-19","设计模式修炼之路 ⑲ 状态模式——状态驱动行为","状态驱动行为、流转规则住进状态家、Thread 状态机、状态 vs 策略终局对决","2113897628365230081",1,1320,6715,17,"2024-09-02T16:09:32","> 行为型第六站。\n\n上一篇结尾留了个场景：电商订单。\"待支付\"的订单点取消，直接关单连退款都不用；\"已发货\"的订单点取消，只能走退货流程；\"已取消\"的订单点什么都没用。**同一个按钮，状态不同，行为完全不同。**\n\n这一篇就把订单这套流转讲透——状态模式，把\"如果\u002F否则\"炼成\"对象\"的终极形态。\n\n## 一、状态模式是什么\n\nGoF 的原始定义：\n\n> Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.\n> （允许对象在内部状态改变时改变它的行为，对象看起来好像修改了它的类。）\n\n翻译成人话：**把每种状态做成一个类，行为跟着状态走。**同一个方法调用，状态对象不一样，执行的行为就不一样——外面看起来，订单对象像是\"整个换了一个类\"。\n\n两个核心角色：\n\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>角色\u003C\u002Fth>\u003Cth>干什么\u003C\u002Fth>\u003Cth>本文对应\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\u003Ctr>\u003Ctd>State（抽象状态）\u003C\u002Ftd>\u003Ctd>定义该状态下所有动作的接口\u003C\u002Ftd>\u003Ctd>OrderState\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>ConcreteState（具体状态）\u003C\u002Ftd>\u003Ctd>每个动作：放行并流转，或者就地拒绝\u003C\u002Ftd>\u003Ctd>待支付\u002F已支付\u002F已发货\u002F已取消\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>Context（上下文）\u003C\u002Ftd>\u003Ctd>持有当前状态，把动作原样委托出去\u003C\u002Ftd>\u003Ctd>OrderContext\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\n\n类比还是订单本身：与其让订单对象背着一本《全状态操作手册》每次办事前翻一遍（if-else），不如给订单配一个\"当前状态客服\"——你说\"取消\"，客服看自己是哪个状态的：待支付客服直接关单，已发货客服让你走退货。**订单自己永远只说一句话：\"这事儿找我当前状态的客服。\"**\n\n## 二、优点与缺点\n\n**优点：**\n\n1. **状态判定本地化**。每种状态只在自己的类里写自己的规则——待支付类里再也不会出现\"已发货该怎么办\"的分支。改哪个状态的行为，就打开哪个状态的类，一目了然。\n2. **新增状态是加法不是改法**。新加一个\"已完成\"状态，写一个新类，只需要让相邻状态知道它——不用把所有方法的所有分支全部过一遍。\n3. **消灭非法状态下的灵异操作**。\"已取消的订单还在发货\"这种 bug，在 if-else 版本里靠人肉细心防守，在状态模式里由类型系统保证——CancelledState 的 ship 方法里就一句提示，想发都发不出去。\n4. **状态流转与业务行为内聚**。流转规则（支付后去已支付）写在状态自己的类里，规则和行为住在一起，而不是散落在十几个 if 分支里。\n\n**缺点：**\n\n1. **类数量膨胀**。一个状态一个类，状态多了类也多——和命令模式 ⑱ 同款\"病\"。\n2. **流转规则分散**。if-else 版本虽然丑，但所有流转集中一眼看完；状态模式里流转规则散落在各个状态类里，想看全貌得画图。\n3. **状态间耦合**。UnpaidState 要 new PaidState——状态类之间互相认识。流转图一复杂，状态类之间容易缠成一团。\n4. **简单场景杀鸡用牛刀**。两三个状态、每个状态就一两条规则，一个 enum 加 switch 就够了，硬上状态模式纯属自虐。\n\n## 三、什么时候用\n\n判断标准：**对象的行为随状态改变，而且状态之间存在流转关系、状态数量会增长、非法操作必须被可靠拦截。**\n\n- **订单流转**：待支付\u002F已支付\u002F已发货\u002F已完成\u002F已取消——本文场景\n- **审批流**：草稿→提交→审核中→通过\u002F驳回，工作流引擎的看家本领\n- **连接生命周期**：TCP 三次握手四次挥手那张状态图\n- **游戏 AI**：怪物巡逻→发现玩家→追击→攻击→返回，经典有限状态机\n- **播放器\u002F电梯\u002F红绿灯**：所有\"按状态出牌\"的设备逻辑\n\n反面对照：状态固定且少（两三个）——enum + switch 足够；各分支是平行算法而非流转关系——那是策略模式 ⑮；分支是在选处理者而非改行为——那是责任链 ⑰。\n\n## 四、实现方式：订单状态机\n\n### 4.1 反面教材：if-else 状态机\n\n状态用 String 存着，每个方法先问一遍\"现在是什么状态\"：\n\n```java\npublic class BadOrder {\n\n    private String state = \"待支付\";\n\n    public void pay() {\n        if (state.equals(\"待支付\")) {\n            state = \"已支付\";\n            System.out.println(\"  支付成功，当前状态：已支付\");\n        } else if (state.equals(\"已支付\")) {\n            System.out.println(\"  该订单已支付，请勿重复支付\");\n        } else if (state.equals(\"已发货\")) {\n            System.out.println(\"  订单已发货，无需支付\");\n        } else if (state.equals(\"已取消\")) {\n            System.out.println(\"  订单已取消，无法支付\");\n        }\n    }\n\n    public void cancel() {\n        if (state.equals(\"待支付\")) {\n            state = \"已取消\";\n            System.out.println(\"  未支付直接关单，当前状态：已取消\");\n        } else if (state.equals(\"已支付\")) {\n            state = \"已取消\";\n            System.out.println(\"  走退款流程，当前状态：已取消\");\n        } else if (state.equals(\"已发货\")) {\n            System.out.println(\"  已发货订单不能直接取消，请走退货流程\");\n        } else if (state.equals(\"已取消\")) {\n            System.out.println(\"  订单已取消，请勿重复操作\");\n        }\n    }\n\n    public void ship() {\n        if (state.equals(\"待支付\")) {\n            System.out.println(\"  未支付订单不能发货\");\n        } else if (state.equals(\"已支付\")) {\n            state = \"已发货\";\n            System.out.println(\"  发货成功，当前状态：已发货\");\n        } else if (state.equals(\"已发货\")) {\n            System.out.println(\"  订单已发货，请勿重复发货\");\n        } else if (state.equals(\"已取消\")) {\n            System.out.println(\"  订单已取消，无法发货\");\n        }\n    }\n\n    \u002F\u002F 状态是 String，流转规则全靠每个方法里的人肉 if-else 对齐：\n    \u002F\u002F 新加一个\"已完成\"状态？三个方法全要改，十二个分支挨个检查，\n    \u002F\u002F 漏掉任何一处，就会出现\"已取消的订单还在发货\"这种灵异事件\n}\n```\n\n病灶拆解：三个方法 × 四个状态 = **十二个分支**，而且这个数字是乘法关系增长的——状态加到六个、动作加到五个，就是三十个分支的矩阵。更阴险的是，这个矩阵没有任何东西帮你对齐：某次需求改了\"已发货不能取消\"的文案，你只想改一处，却必须在三个方法里分别确认有没有散落的同款逻辑。\n\n### 4.2 正解：每种状态一个类\n\n第一步，抽象状态——把订单的所有动作定义为接口：\n\n```java\npublic interface OrderState {\n\n    void pay(OrderContext order);\n\n    void cancel(OrderContext order);\n\n    void ship(OrderContext order);\n}\n```\n\n第二步，上下文。注意它有多\"闲\"——三个动作全是原样转发：\n\n```java\npublic class OrderContext {\n\n    private final String orderId;\n    private OrderState state;\n\n    public OrderContext(String orderId) {\n        this.orderId = orderId;\n        this.state = new UnpaidState();\n        System.out.println(\"  订单 \" + orderId + \" 下单成功，当前状态：待支付\");\n    }\n\n    public void pay() { state.pay(this); }\n    public void cancel() { state.cancel(this); }\n    public void ship() { state.ship(this); }\n\n    public void setState(OrderState state) { this.state = state; }\n    public String getOrderId() { return orderId; }\n}\n```\n\n第三步，具体状态。待支付状态：支付放行、取消放行、发货拒绝——流转规则写在状态自己家里：\n\n```java\npublic class UnpaidState implements OrderState {\n\n    @Override\n    public void pay(OrderContext order) {\n        order.setState(new PaidState());\n        System.out.println(\"  [待支付] 支付成功，流转：待支付 → 已支付\");\n    }\n\n    @Override\n    public void cancel(OrderContext order) {\n        order.setState(new CancelledState());\n        System.out.println(\"  [待支付] 未支付直接关单，流转：待支付 → 已取消\");\n    }\n\n    @Override\n    public void ship(OrderContext order) {\n        System.out.println(\"  [待支付] 未支付订单不能发货\");\n    }\n}\n```\n\n已支付（重复支付拒绝、取消走退款、发货放行）、已发货（取消只能走退货流程）、已取消（全部拒绝）三个状态类结构完全相同，各自安放各自的规则。\n\n最后测试——重点看两笔订单的对照：订单 A 一路走到已发货再取消，订单 B 下单就反悔：\n\n```java\npublic class StateTest {\n\n    public static void main(String[] args) {\n        System.out.println(\"========== 反面教材：if-else 状态机 ==========\");\n        BadOrder bad = new BadOrder();\n        bad.pay();\n        bad.pay();\n        bad.ship();\n        bad.cancel();\n\n        System.out.println();\n        System.out.println(\"========== 正解：状态模式 ==========\");\n\n        System.out.println(\"-- 订单A：一路走下去，再想取消 --\");\n        OrderContext orderA = new OrderContext(\"A-2024-001\");\n        orderA.pay();\n        orderA.ship();\n        orderA.cancel();\n\n        System.out.println(\"-- 订单B：下单就反悔 --\");\n        OrderContext orderB = new OrderContext(\"B-2024-002\");\n        orderB.cancel();\n        orderB.pay();\n        orderB.ship();\n    }\n}\n```\n\n运行输出：\n\n```text\n========== 反面教材：if-else 状态机 ==========\n  支付成功，当前状态：已支付\n  该订单已支付，请勿重复支付\n  发货成功，当前状态：已发货\n  已发货订单不能直接取消，请走退货流程\n\n========== 正解：状态模式 ==========\n-- 订单A：一路走下去，再想取消 --\n  订单 A-2024-001 下单成功，当前状态：待支付\n  [待支付] 支付成功，流转：待支付 → 已支付\n  [已支付] 发货成功，流转：已支付 → 已发货\n  [已发货] 已发货不能直接取消，请走退货流程\n-- 订单B：下单就反悔 --\n  订单 B-2024-002 下单成功，当前状态：待支付\n  [待支付] 未支付直接关单，流转：待支付 → 已取消\n  [已取消] 订单已取消，无法支付\n  [已取消] 订单已取消，无法发货\n```\n\n### 4.3 三处细节咂摸一下\n\n**细节一：流转规则搬家了。**`order.setState(new PaidState())` 写在 UnpaidState 内部——\"我支付之后去哪\"由我自己决定，不再由上下文的某个 if 分支决定。每个状态类就是流转图上的一个节点，节点只画自己出边的箭头。想看完整流转图？把每个状态类的方法扫一遍，出边就全齐了。\n\n**细节二：同一个 cancel()，三种命运。**订单 A 在已发货状态取消，被引导去退货流程；订单 B 在待支付状态取消，直接关单；若在已支付状态，则会走退款。客户端三次调用的代码一模一样，行为差异完全由\"当前是谁在值班\"决定——这就是\"状态驱动行为\"，也是定义里\"对象看起来好像修改了它的类\"的真身：同一个 OrderContext，状态一换，整个行为模式全变。\n\n**细节三：上下文被架空成转发器，这是设计而不是偷懒。**OrderContext 的三个方法清一色 `state.xxx(this)`——它存在的意义只剩\"持有当前状态 + 提供身份信息\"。有人嫌这层委托多余想砍掉，砍了之后调用方就得自己持有状态对象、自己管流转，状态模式的封装就破了。上下文是外界和状态机之间的防火墙。\n\n### 4.4 开闭原则的成色\n\n诚实地说：状态模式对开闭原则的兑现是\"打折扣\"的。新加\"已完成\"状态，确实不用改 if-else 矩阵了，但 ShippedState 的收货动作、CancelledState……凡是流转图上有边指向新状态的旧状态类，都得动。\n\n它的真实收益是：**把\"改\"的范围从\"全方法全分支\"压缩到\"相邻状态类\"。**改动的爆炸半径从 O(方法数 × 状态数) 缩到 O(相邻状态数)——这就是那张流转图的意义：图越长越密，状态模式越划算；三两个状态的小图，enum 加 switch 反而是更好的工程选择。\n\n## 五、在框架里找它\n\n**Thread——JDK 亲手实现的状态机。**`Thread.State` 枚举：NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED，六种状态一张流转图。而你早就体验过\"状态驱动行为\"：`start()` 只在 NEW 状态合法，对同一个线程调第二次 start 会抛 `IllegalThreadStateException`——\"已启动的线程不能再启动\"，这正是 CancelledState 拦截 ship 的同款逻辑。顺带注意 `Thread.State` 是 enum：无字段的状态类用枚举实现，天然单例（② 单例模式的老朋友），本文四个状态类同样无字段，工程上完全可以枚举化。\n\n**Spring StateMachine。**Spring 官方状态机框架：Builder 里声明状态集合、事件集合、流转规则、流转守卫（条件放行）、动作监听器。订单、支付网关、物联网设备状态，状态超过十个、流转带条件时，它比手写状态类省心得多——本篇手写状态类是为看懂骨架，生产里复杂场景请直接上框架。\n\n**工作流引擎。**Activiti、Flowable、Camunda 把\"状态+流转\"做成了数据库表 + 流程图 DSL：审批流的每个节点是一个状态，审批通过是流转事件，会签、驳回、超时自动流转都是引擎内置能力。企业级审批系统的终极形态。\n\n**TCP 协议栈。**三次握手、四次挥手那张经典状态图（CLOSED→SYN_SENT→ESTABLISHED→FIN_WAIT……），就是操作系统内核里一台真实运转的状态机——状态模式的威力，撑得起整个互联网的连接管理。\n\n一句话总结这一节：**凡是\"行为随状态变、状态按图流转\"的地方，从 JDK 到内核到工作流，状态机无处不在；状态模式就是状态机的面向对象实现。**\n\n## 六、状态 vs 策略：孪生兄弟的最后一战\n\n⑱ 讲命令 vs 策略时留了个尾巴：状态模式和策略模式结构也几乎一样——接口、一堆实现类、一个持有者。这两个是 GoF 二十三式里最著名的孪生兄弟，系列走到这里，最后一战做个了断：\n\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>\u003C\u002Fth>\u003Cth>策略模式 ⑮\u003C\u002Fth>\u003Cth>状态模式 ⑲\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\u003Ctr>\u003Ctd>一堆实现类是什么\u003C\u002Ftd>\u003Ctd>平行的**算法**\u003C\u002Ftd>\u003Ctd>流转图上的**节点**\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>实现类之间认识吗\u003C\u002Ftd>\u003Ctd>互不相识\u003C\u002Ftd>\u003Ctd>相识（知道流转目标是谁）\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>谁来切换\u003C\u002Ftd>\u003Ctd>**外部**指定（调用方\u002F工厂）\u003C\u002Ftd>\u003Ctd>**内部**流转（状态类自己 setState）\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>切换像什么\u003C\u002Ftd>\u003Ctd>换插槽上的卡\u003C\u002Ftd>\u003Ctd>图上的节点跳转\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>关心历史吗\u003C\u002Ftd>\u003Ctd>不关心\u003C\u002Ftd>\u003Ctd>当前状态就是全部历史沉淀\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\n\n最狠的分辨方法来了：**把 UnpaidState 里那句 `order.setState(new PaidState())` 删掉，状态模式瞬间退化成策略模式。**分水岭就一句话——**有没有流转**。策略是一排平行选项，选哪个、何时换，外面的世界说了算；状态是一张有向图，走哪条边，图上的规则说了算。\n\n面试官问\"状态和策略有啥区别\"，先答这张表，再补一句\"删除 setState 调用、状态模式就变成策略模式\"，基本稳了。\n\n## 写在最后\n\n状态模式一句话总结：**每种状态一个类，行为跟着状态走；流转规则住进状态自己家，非法操作在源头就被拦下。**\n\n下一篇 ⑳ 迭代器模式——轻松一点的话题。你每天都在写 `for (String s : list)`，有没有想过这个冒号背后是谁在工作？为什么 ArrayList、HashSet、LinkedList 都能被 for-each，而你自己写的类就不行？怎么让你自己的集合也享受同款待遇？**统一的遍历协议——迭代器模式，下一篇见。**\n","\u003Cblockquote>\n \u003Cp>行为型第六站。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>上一篇结尾留了个场景：电商订单。\"待支付\"的订单点取消，直接关单连退款都不用；\"已发货\"的订单点取消，只能走退货流程；\"已取消\"的订单点什么都没用。\u003Cstrong>同一个按钮，状态不同，行为完全不同。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>这一篇就把订单这套流转讲透——状态模式，把\"如果\u002F否则\"炼成\"对象\"的终极形态。\u003C\u002Fp>\n\u003Ch2>一、状态模式是什么\u003C\u002Fh2>\n\u003Cp>GoF 的原始定义：\u003C\u002Fp>\n\u003Cblockquote>\n \u003Cp>Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. （允许对象在内部状态改变时改变它的行为，对象看起来好像修改了它的类。）\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>翻译成人话：**把每种状态做成一个类，行为跟着状态走。**同一个方法调用，状态对象不一样，执行的行为就不一样——外面看起来，订单对象像是\"整个换了一个类\"。\u003C\u002Fp>\n\u003Cp>两个核心角色：\u003C\u002Fp>\n\u003Ctable>\n \u003Cthead>\n  \u003Ctr>\n   \u003Cth>角色\u003C\u002Fth>\n   \u003Cth>干什么\u003C\u002Fth>\n   \u003Cth>本文对应\u003C\u002Fth>\n  \u003C\u002Ftr>\n \u003C\u002Fthead>\n \u003Ctbody>\n  \u003Ctr>\n   \u003Ctd>State（抽象状态）\u003C\u002Ftd>\n   \u003Ctd>定义该状态下所有动作的接口\u003C\u002Ftd>\n   \u003Ctd>OrderState\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>ConcreteState（具体状态）\u003C\u002Ftd>\n   \u003Ctd>每个动作：放行并流转，或者就地拒绝\u003C\u002Ftd>\n   \u003Ctd>待支付\u002F已支付\u002F已发货\u002F已取消\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>Context（上下文）\u003C\u002Ftd>\n   \u003Ctd>持有当前状态，把动作原样委托出去\u003C\u002Ftd>\n   \u003Ctd>OrderContext\u003C\u002Ftd>\n  \u003C\u002Ftr>\n \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>类比还是订单本身：与其让订单对象背着一本《全状态操作手册》每次办事前翻一遍（if-else），不如给订单配一个\"当前状态客服\"——你说\"取消\"，客服看自己是哪个状态的：待支付客服直接关单，已发货客服让你走退货。\u003Cstrong>订单自己永远只说一句话：\"这事儿找我当前状态的客服。\"\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Ch2>二、优点与缺点\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>优点：\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Col>\n \u003Cli>\u003Cstrong>状态判定本地化\u003C\u002Fstrong>。每种状态只在自己的类里写自己的规则——待支付类里再也不会出现\"已发货该怎么办\"的分支。改哪个状态的行为，就打开哪个状态的类，一目了然。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>新增状态是加法不是改法\u003C\u002Fstrong>。新加一个\"已完成\"状态，写一个新类，只需要让相邻状态知道它——不用把所有方法的所有分支全部过一遍。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>消灭非法状态下的灵异操作\u003C\u002Fstrong>。\"已取消的订单还在发货\"这种 bug，在 if-else 版本里靠人肉细心防守，在状态模式里由类型系统保证——CancelledState 的 ship 方法里就一句提示，想发都发不出去。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>状态流转与业务行为内聚\u003C\u002Fstrong>。流转规则（支付后去已支付）写在状态自己的类里，规则和行为住在一起，而不是散落在十几个 if 分支里。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>\u003Cstrong>缺点：\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Col>\n \u003Cli>\u003Cstrong>类数量膨胀\u003C\u002Fstrong>。一个状态一个类，状态多了类也多——和命令模式 ⑱ 同款\"病\"。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>流转规则分散\u003C\u002Fstrong>。if-else 版本虽然丑，但所有流转集中一眼看完；状态模式里流转规则散落在各个状态类里，想看全貌得画图。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>状态间耦合\u003C\u002Fstrong>。UnpaidState 要 new PaidState——状态类之间互相认识。流转图一复杂，状态类之间容易缠成一团。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>简单场景杀鸡用牛刀\u003C\u002Fstrong>。两三个状态、每个状态就一两条规则，一个 enum 加 switch 就够了，硬上状态模式纯属自虐。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2>三、什么时候用\u003C\u002Fh2>\n\u003Cp>判断标准：\u003Cstrong>对象的行为随状态改变，而且状态之间存在流转关系、状态数量会增长、非法操作必须被可靠拦截。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cul>\n \u003Cli>\u003Cstrong>订单流转\u003C\u002Fstrong>：待支付\u002F已支付\u002F已发货\u002F已完成\u002F已取消——本文场景\u003C\u002Fli>\n \u003Cli>\u003Cstrong>审批流\u003C\u002Fstrong>：草稿→提交→审核中→通过\u002F驳回，工作流引擎的看家本领\u003C\u002Fli>\n \u003Cli>\u003Cstrong>连接生命周期\u003C\u002Fstrong>：TCP 三次握手四次挥手那张状态图\u003C\u002Fli>\n \u003Cli>\u003Cstrong>游戏 AI\u003C\u002Fstrong>：怪物巡逻→发现玩家→追击→攻击→返回，经典有限状态机\u003C\u002Fli>\n \u003Cli>\u003Cstrong>播放器\u002F电梯\u002F红绿灯\u003C\u002Fstrong>：所有\"按状态出牌\"的设备逻辑\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>反面对照：状态固定且少（两三个）——enum + switch 足够；各分支是平行算法而非流转关系——那是策略模式 ⑮；分支是在选处理者而非改行为——那是责任链 ⑰。\u003C\u002Fp>\n\u003Ch2>四、实现方式：订单状态机\u003C\u002Fh2>\n\u003Ch3>4.1 反面教材：if-else 状态机\u003C\u002Fh3>\n\u003Cp>状态用 String 存着，每个方法先问一遍\"现在是什么状态\"：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">public class BadOrder {\n\n    private String state = \"待支付\";\n\n    public void pay() {\n        if (state.equals(\"待支付\")) {\n            state = \"已支付\";\n            System.out.println(\"  支付成功，当前状态：已支付\");\n        } else if (state.equals(\"已支付\")) {\n            System.out.println(\"  该订单已支付，请勿重复支付\");\n        } else if (state.equals(\"已发货\")) {\n            System.out.println(\"  订单已发货，无需支付\");\n        } else if (state.equals(\"已取消\")) {\n            System.out.println(\"  订单已取消，无法支付\");\n        }\n    }\n\n    public void cancel() {\n        if (state.equals(\"待支付\")) {\n            state = \"已取消\";\n            System.out.println(\"  未支付直接关单，当前状态：已取消\");\n        } else if (state.equals(\"已支付\")) {\n            state = \"已取消\";\n            System.out.println(\"  走退款流程，当前状态：已取消\");\n        } else if (state.equals(\"已发货\")) {\n            System.out.println(\"  已发货订单不能直接取消，请走退货流程\");\n        } else if (state.equals(\"已取消\")) {\n            System.out.println(\"  订单已取消，请勿重复操作\");\n        }\n    }\n\n    public void ship() {\n        if (state.equals(\"待支付\")) {\n            System.out.println(\"  未支付订单不能发货\");\n        } else if (state.equals(\"已支付\")) {\n            state = \"已发货\";\n            System.out.println(\"  发货成功，当前状态：已发货\");\n        } else if (state.equals(\"已发货\")) {\n            System.out.println(\"  订单已发货，请勿重复发货\");\n        } else if (state.equals(\"已取消\")) {\n            System.out.println(\"  订单已取消，无法发货\");\n        }\n    }\n\n    \u002F\u002F 状态是 String，流转规则全靠每个方法里的人肉 if-else 对齐：\n    \u002F\u002F 新加一个\"已完成\"状态？三个方法全要改，十二个分支挨个检查，\n    \u002F\u002F 漏掉任何一处，就会出现\"已取消的订单还在发货\"这种灵异事件\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>病灶拆解：三个方法 × 四个状态 = \u003Cstrong>十二个分支\u003C\u002Fstrong>，而且这个数字是乘法关系增长的——状态加到六个、动作加到五个，就是三十个分支的矩阵。更阴险的是，这个矩阵没有任何东西帮你对齐：某次需求改了\"已发货不能取消\"的文案，你只想改一处，却必须在三个方法里分别确认有没有散落的同款逻辑。\u003C\u002Fp>\n\u003Ch3>4.2 正解：每种状态一个类\u003C\u002Fh3>\n\u003Cp>第一步，抽象状态——把订单的所有动作定义为接口：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">public interface OrderState {\n\n    void pay(OrderContext order);\n\n    void cancel(OrderContext order);\n\n    void ship(OrderContext order);\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>第二步，上下文。注意它有多\"闲\"——三个动作全是原样转发：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">public class OrderContext {\n\n    private final String orderId;\n    private OrderState state;\n\n    public OrderContext(String orderId) {\n        this.orderId = orderId;\n        this.state = new UnpaidState();\n        System.out.println(\"  订单 \" + orderId + \" 下单成功，当前状态：待支付\");\n    }\n\n    public void pay() { state.pay(this); }\n    public void cancel() { state.cancel(this); }\n    public void ship() { state.ship(this); }\n\n    public void setState(OrderState state) { this.state = state; }\n    public String getOrderId() { return orderId; }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>第三步，具体状态。待支付状态：支付放行、取消放行、发货拒绝——流转规则写在状态自己家里：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">public class UnpaidState implements OrderState {\n\n    @Override\n    public void pay(OrderContext order) {\n        order.setState(new PaidState());\n        System.out.println(\"  [待支付] 支付成功，流转：待支付 → 已支付\");\n    }\n\n    @Override\n    public void cancel(OrderContext order) {\n        order.setState(new CancelledState());\n        System.out.println(\"  [待支付] 未支付直接关单，流转：待支付 → 已取消\");\n    }\n\n    @Override\n    public void ship(OrderContext order) {\n        System.out.println(\"  [待支付] 未支付订单不能发货\");\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>已支付（重复支付拒绝、取消走退款、发货放行）、已发货（取消只能走退货流程）、已取消（全部拒绝）三个状态类结构完全相同，各自安放各自的规则。\u003C\u002Fp>\n\u003Cp>最后测试——重点看两笔订单的对照：订单 A 一路走到已发货再取消，订单 B 下单就反悔：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">public class StateTest {\n\n    public static void main(String[] args) {\n        System.out.println(\"========== 反面教材：if-else 状态机 ==========\");\n        BadOrder bad = new BadOrder();\n        bad.pay();\n        bad.pay();\n        bad.ship();\n        bad.cancel();\n\n        System.out.println();\n        System.out.println(\"========== 正解：状态模式 ==========\");\n\n        System.out.println(\"-- 订单A：一路走下去，再想取消 --\");\n        OrderContext orderA = new OrderContext(\"A-2024-001\");\n        orderA.pay();\n        orderA.ship();\n        orderA.cancel();\n\n        System.out.println(\"-- 订单B：下单就反悔 --\");\n        OrderContext orderB = new OrderContext(\"B-2024-002\");\n        orderB.cancel();\n        orderB.pay();\n        orderB.ship();\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>运行输出：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">========== 反面教材：if-else 状态机 ==========\n  支付成功，当前状态：已支付\n  该订单已支付，请勿重复支付\n  发货成功，当前状态：已发货\n  已发货订单不能直接取消，请走退货流程\n\n========== 正解：状态模式 ==========\n-- 订单A：一路走下去，再想取消 --\n  订单 A-2024-001 下单成功，当前状态：待支付\n  [待支付] 支付成功，流转：待支付 → 已支付\n  [已支付] 发货成功，流转：已支付 → 已发货\n  [已发货] 已发货不能直接取消，请走退货流程\n-- 订单B：下单就反悔 --\n  订单 B-2024-002 下单成功，当前状态：待支付\n  [待支付] 未支付直接关单，流转：待支付 → 已取消\n  [已取消] 订单已取消，无法支付\n  [已取消] 订单已取消，无法发货\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>4.3 三处细节咂摸一下\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>细节一：流转规则搬家了。\u003C\u002Fstrong>\u003Ccode>order.setState(new PaidState())\u003C\u002Fcode> 写在 UnpaidState 内部——\"我支付之后去哪\"由我自己决定，不再由上下文的某个 if 分支决定。每个状态类就是流转图上的一个节点，节点只画自己出边的箭头。想看完整流转图？把每个状态类的方法扫一遍，出边就全齐了。\u003C\u002Fp>\n\u003Cp>**细节二：同一个 cancel()，三种命运。**订单 A 在已发货状态取消，被引导去退货流程；订单 B 在待支付状态取消，直接关单；若在已支付状态，则会走退款。客户端三次调用的代码一模一样，行为差异完全由\"当前是谁在值班\"决定——这就是\"状态驱动行为\"，也是定义里\"对象看起来好像修改了它的类\"的真身：同一个 OrderContext，状态一换，整个行为模式全变。\u003C\u002Fp>\n\u003Cp>**细节三：上下文被架空成转发器，这是设计而不是偷懒。**OrderContext 的三个方法清一色 \u003Ccode>state.xxx(this)\u003C\u002Fcode>——它存在的意义只剩\"持有当前状态 + 提供身份信息\"。有人嫌这层委托多余想砍掉，砍了之后调用方就得自己持有状态对象、自己管流转，状态模式的封装就破了。上下文是外界和状态机之间的防火墙。\u003C\u002Fp>\n\u003Ch3>4.4 开闭原则的成色\u003C\u002Fh3>\n\u003Cp>诚实地说：状态模式对开闭原则的兑现是\"打折扣\"的。新加\"已完成\"状态，确实不用改 if-else 矩阵了，但 ShippedState 的收货动作、CancelledState……凡是流转图上有边指向新状态的旧状态类，都得动。\u003C\u002Fp>\n\u003Cp>它的真实收益是：**把\"改\"的范围从\"全方法全分支\"压缩到\"相邻状态类\"。**改动的爆炸半径从 O(方法数 × 状态数) 缩到 O(相邻状态数)——这就是那张流转图的意义：图越长越密，状态模式越划算；三两个状态的小图，enum 加 switch 反而是更好的工程选择。\u003C\u002Fp>\n\u003Ch2>五、在框架里找它\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Thread——JDK 亲手实现的状态机。\u003C\u002Fstrong>\u003Ccode>Thread.State\u003C\u002Fcode> 枚举：NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED，六种状态一张流转图。而你早就体验过\"状态驱动行为\"：\u003Ccode>start()\u003C\u002Fcode> 只在 NEW 状态合法，对同一个线程调第二次 start 会抛 \u003Ccode>IllegalThreadStateException\u003C\u002Fcode>——\"已启动的线程不能再启动\"，这正是 CancelledState 拦截 ship 的同款逻辑。顺带注意 \u003Ccode>Thread.State\u003C\u002Fcode> 是 enum：无字段的状态类用枚举实现，天然单例（② 单例模式的老朋友），本文四个状态类同样无字段，工程上完全可以枚举化。\u003C\u002Fp>\n\u003Cp>**Spring StateMachine。**Spring 官方状态机框架：Builder 里声明状态集合、事件集合、流转规则、流转守卫（条件放行）、动作监听器。订单、支付网关、物联网设备状态，状态超过十个、流转带条件时，它比手写状态类省心得多——本篇手写状态类是为看懂骨架，生产里复杂场景请直接上框架。\u003C\u002Fp>\n\u003Cp>**工作流引擎。**Activiti、Flowable、Camunda 把\"状态+流转\"做成了数据库表 + 流程图 DSL：审批流的每个节点是一个状态，审批通过是流转事件，会签、驳回、超时自动流转都是引擎内置能力。企业级审批系统的终极形态。\u003C\u002Fp>\n\u003Cp>**TCP 协议栈。**三次握手、四次挥手那张经典状态图（CLOSED→SYN_SENT→ESTABLISHED→FIN_WAIT……），就是操作系统内核里一台真实运转的状态机——状态模式的威力，撑得起整个互联网的连接管理。\u003C\u002Fp>\n\u003Cp>一句话总结这一节：\u003Cstrong>凡是\"行为随状态变、状态按图流转\"的地方，从 JDK 到内核到工作流，状态机无处不在；状态模式就是状态机的面向对象实现。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Ch2>六、状态 vs 策略：孪生兄弟的最后一战\u003C\u002Fh2>\n\u003Cp>⑱ 讲命令 vs 策略时留了个尾巴：状态模式和策略模式结构也几乎一样——接口、一堆实现类、一个持有者。这两个是 GoF 二十三式里最著名的孪生兄弟，系列走到这里，最后一战做个了断：\u003C\u002Fp>\n\u003Ctable>\n \u003Cthead>\n  \u003Ctr>\n   \u003Cth>\u003C\u002Fth>\n   \u003Cth>策略模式 ⑮\u003C\u002Fth>\n   \u003Cth>状态模式 ⑲\u003C\u002Fth>\n  \u003C\u002Ftr>\n \u003C\u002Fthead>\n \u003Ctbody>\n  \u003Ctr>\n   \u003Ctd>一堆实现类是什么\u003C\u002Ftd>\n   \u003Ctd>平行的**算法**\u003C\u002Ftd>\n   \u003Ctd>流转图上的**节点**\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>实现类之间认识吗\u003C\u002Ftd>\n   \u003Ctd>互不相识\u003C\u002Ftd>\n   \u003Ctd>相识（知道流转目标是谁）\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>谁来切换\u003C\u002Ftd>\n   \u003Ctd>**外部**指定（调用方\u002F工厂）\u003C\u002Ftd>\n   \u003Ctd>**内部**流转（状态类自己 setState）\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>切换像什么\u003C\u002Ftd>\n   \u003Ctd>换插槽上的卡\u003C\u002Ftd>\n   \u003Ctd>图上的节点跳转\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>关心历史吗\u003C\u002Ftd>\n   \u003Ctd>不关心\u003C\u002Ftd>\n   \u003Ctd>当前状态就是全部历史沉淀\u003C\u002Ftd>\n  \u003C\u002Ftr>\n \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>最狠的分辨方法来了：**把 UnpaidState 里那句 \u003Ccode>order.setState(new PaidState())\u003C\u002Fcode> 删掉，状态模式瞬间退化成策略模式。**分水岭就一句话——\u003Cstrong>有没有流转\u003C\u002Fstrong>。策略是一排平行选项，选哪个、何时换，外面的世界说了算；状态是一张有向图，走哪条边，图上的规则说了算。\u003C\u002Fp>\n\u003Cp>面试官问\"状态和策略有啥区别\"，先答这张表，再补一句\"删除 setState 调用、状态模式就变成策略模式\"，基本稳了。\u003C\u002Fp>\n\u003Ch2>写在最后\u003C\u002Fh2>\n\u003Cp>状态模式一句话总结：\u003Cstrong>每种状态一个类，行为跟着状态走；流转规则住进状态自己家，非法操作在源头就被拦下。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>下一篇 ⑳ 迭代器模式——轻松一点的话题。你每天都在写 \u003Ccode>for (String s : list)\u003C\u002Fcode>，有没有想过这个冒号背后是谁在工作？为什么 ArrayList、HashSet、LinkedList 都能被 for-each，而你自己写的类就不行？怎么让你自己的集合也享受同款待遇？\u003Cstrong>统一的遍历协议——迭代器模式，下一篇见。\u003C\u002Fstrong>\u003C\u002Fp>",[],"index,follow",null,{"code":4,"message":5,"data":44},[45,54,63],{"id":46,"slug":47,"title":48,"summary":49,"coverUrl":9,"categoryId":32,"status":33,"topFlag":4,"viewCount":50,"likeCount":4,"wordCount":51,"readingMinutes":52,"publishedAt":53},"2113931965261418497","design-pattern-15","设计模式修炼之路 ⑮ 策略模式——if-else 的解药","一个分支一个类、工厂加 Map 连选择处都不写 if-else、Spring 自动收集、线程池拒绝策略",1681,7953,20,"2024-06-16T18:10:44",{"id":55,"slug":56,"title":57,"summary":58,"coverUrl":9,"categoryId":32,"status":33,"topFlag":4,"viewCount":59,"likeCount":4,"wordCount":60,"readingMinutes":61,"publishedAt":62},"2113932151245246465","design-pattern-16","设计模式修炼之路 ⑯ 观察者模式——事件订阅与广播","一对多依赖、订阅退订自由、生产环境三块拼图、Spring 事件机制、观察者 vs 发布订阅",3150,6331,16,"2024-07-07T14:57:29",{"id":64,"slug":65,"title":66,"summary":67,"coverUrl":9,"categoryId":32,"status":33,"topFlag":4,"viewCount":68,"likeCount":4,"wordCount":69,"readingMinutes":70,"publishedAt":71},"2113932265082851329","design-pattern-18","设计模式修炼之路 ⑱ 命令模式——把请求封装成对象","把请求封装成对象、增量式撤销、白送的排队与重放、Executor 的铁证",2089,7080,18,"2024-08-09T12:36:13",{"code":4,"message":5,"data":73},{"prev":74,"next":75},{"id":64,"slug":65,"title":66,"summary":67,"coverUrl":9,"categoryId":32,"status":33,"topFlag":4,"viewCount":68,"likeCount":4,"wordCount":69,"readingMinutes":70,"publishedAt":71},{"id":76,"slug":77,"title":78,"summary":79,"coverUrl":9,"categoryId":32,"status":33,"topFlag":4,"viewCount":80,"likeCount":4,"wordCount":81,"readingMinutes":36,"publishedAt":82},"2113933908541181954","design-pattern-20","设计模式修炼之路 ⑳ 迭代器模式——统一遍历协议","统一遍历协议、for-each 只是语法糖、手写可迭代书架、fail-fast 的哲学",2334,6515,"2024-09-17T21:25:50",{"code":4,"message":5,"data":84},[85,91,96,102],{"id":86,"name":87,"slug":88,"description":89,"sortOrder":4,"articleCount":90},"1946000000000000001","antlr4","tech","编程与技术笔记","13",{"id":32,"name":92,"slug":93,"description":94,"sortOrder":4,"articleCount":95},"设计模式","design-pattern","设计模式系列","34",{"id":97,"name":98,"slug":99,"description":100,"sortOrder":33,"articleCount":101},"1946000000000000002","随笔","essay","生活与思考","0",{"id":103,"name":104,"slug":105,"description":106,"sortOrder":107,"articleCount":108},"2105200093764698113","工具","tools","好用的工具推荐",2,"1",{"html":110,"toc":111},"\u003Cblockquote>\n \u003Cp>行为型第六站。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>上一篇结尾留了个场景：电商订单。\"待支付\"的订单点取消，直接关单连退款都不用；\"已发货\"的订单点取消，只能走退货流程；\"已取消\"的订单点什么都没用。\u003Cstrong>同一个按钮，状态不同，行为完全不同。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>这一篇就把订单这套流转讲透——状态模式，把\"如果\u002F否则\"炼成\"对象\"的终极形态。\u003C\u002Fp>\n\u003Ch2 id=\"toc-1\">一、状态模式是什么\u003C\u002Fh2>\n\u003Cp>GoF 的原始定义：\u003C\u002Fp>\n\u003Cblockquote>\n \u003Cp>Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. （允许对象在内部状态改变时改变它的行为，对象看起来好像修改了它的类。）\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>翻译成人话：**把每种状态做成一个类，行为跟着状态走。**同一个方法调用，状态对象不一样，执行的行为就不一样——外面看起来，订单对象像是\"整个换了一个类\"。\u003C\u002Fp>\n\u003Cp>两个核心角色：\u003C\u002Fp>\n\u003Ctable>\n \u003Cthead>\n  \u003Ctr>\n   \u003Cth>角色\u003C\u002Fth>\n   \u003Cth>干什么\u003C\u002Fth>\n   \u003Cth>本文对应\u003C\u002Fth>\n  \u003C\u002Ftr>\n \u003C\u002Fthead>\n \u003Ctbody>\n  \u003Ctr>\n   \u003Ctd>State（抽象状态）\u003C\u002Ftd>\n   \u003Ctd>定义该状态下所有动作的接口\u003C\u002Ftd>\n   \u003Ctd>OrderState\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>ConcreteState（具体状态）\u003C\u002Ftd>\n   \u003Ctd>每个动作：放行并流转，或者就地拒绝\u003C\u002Ftd>\n   \u003Ctd>待支付\u002F已支付\u002F已发货\u002F已取消\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>Context（上下文）\u003C\u002Ftd>\n   \u003Ctd>持有当前状态，把动作原样委托出去\u003C\u002Ftd>\n   \u003Ctd>OrderContext\u003C\u002Ftd>\n  \u003C\u002Ftr>\n \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>类比还是订单本身：与其让订单对象背着一本《全状态操作手册》每次办事前翻一遍（if-else），不如给订单配一个\"当前状态客服\"——你说\"取消\"，客服看自己是哪个状态的：待支付客服直接关单，已发货客服让你走退货。\u003Cstrong>订单自己永远只说一句话：\"这事儿找我当前状态的客服。\"\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Ch2 id=\"toc-2\">二、优点与缺点\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>优点：\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Col>\n \u003Cli>\u003Cstrong>状态判定本地化\u003C\u002Fstrong>。每种状态只在自己的类里写自己的规则——待支付类里再也不会出现\"已发货该怎么办\"的分支。改哪个状态的行为，就打开哪个状态的类，一目了然。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>新增状态是加法不是改法\u003C\u002Fstrong>。新加一个\"已完成\"状态，写一个新类，只需要让相邻状态知道它——不用把所有方法的所有分支全部过一遍。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>消灭非法状态下的灵异操作\u003C\u002Fstrong>。\"已取消的订单还在发货\"这种 bug，在 if-else 版本里靠人肉细心防守，在状态模式里由类型系统保证——CancelledState 的 ship 方法里就一句提示，想发都发不出去。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>状态流转与业务行为内聚\u003C\u002Fstrong>。流转规则（支付后去已支付）写在状态自己的类里，规则和行为住在一起，而不是散落在十几个 if 分支里。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>\u003Cstrong>缺点：\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Col>\n \u003Cli>\u003Cstrong>类数量膨胀\u003C\u002Fstrong>。一个状态一个类，状态多了类也多——和命令模式 ⑱ 同款\"病\"。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>流转规则分散\u003C\u002Fstrong>。if-else 版本虽然丑，但所有流转集中一眼看完；状态模式里流转规则散落在各个状态类里，想看全貌得画图。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>状态间耦合\u003C\u002Fstrong>。UnpaidState 要 new PaidState——状态类之间互相认识。流转图一复杂，状态类之间容易缠成一团。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>简单场景杀鸡用牛刀\u003C\u002Fstrong>。两三个状态、每个状态就一两条规则，一个 enum 加 switch 就够了，硬上状态模式纯属自虐。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"toc-3\">三、什么时候用\u003C\u002Fh2>\n\u003Cp>判断标准：\u003Cstrong>对象的行为随状态改变，而且状态之间存在流转关系、状态数量会增长、非法操作必须被可靠拦截。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cul>\n \u003Cli>\u003Cstrong>订单流转\u003C\u002Fstrong>：待支付\u002F已支付\u002F已发货\u002F已完成\u002F已取消——本文场景\u003C\u002Fli>\n \u003Cli>\u003Cstrong>审批流\u003C\u002Fstrong>：草稿→提交→审核中→通过\u002F驳回，工作流引擎的看家本领\u003C\u002Fli>\n \u003Cli>\u003Cstrong>连接生命周期\u003C\u002Fstrong>：TCP 三次握手四次挥手那张状态图\u003C\u002Fli>\n \u003Cli>\u003Cstrong>游戏 AI\u003C\u002Fstrong>：怪物巡逻→发现玩家→追击→攻击→返回，经典有限状态机\u003C\u002Fli>\n \u003Cli>\u003Cstrong>播放器\u002F电梯\u002F红绿灯\u003C\u002Fstrong>：所有\"按状态出牌\"的设备逻辑\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>反面对照：状态固定且少（两三个）——enum + switch 足够；各分支是平行算法而非流转关系——那是策略模式 ⑮；分支是在选处理者而非改行为——那是责任链 ⑰。\u003C\u002Fp>\n\u003Ch2 id=\"toc-4\">四、实现方式：订单状态机\u003C\u002Fh2>\n\u003Ch3 id=\"toc-5\">4.1 反面教材：if-else 状态机\u003C\u002Fh3>\n\u003Cp>状态用 String 存着，每个方法先问一遍\"现在是什么状态\"：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> class\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> BadOrder\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    private\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> String state \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \"待支付\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> pay\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (state.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equals\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"待支付\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            state \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \"已支付\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  支付成功，当前状态：已支付\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        } \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (state.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equals\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"已支付\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  该订单已支付，请勿重复支付\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        } \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (state.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equals\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"已发货\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  订单已发货，无需支付\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        } \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (state.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equals\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"已取消\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  订单已取消，无法支付\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> cancel\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (state.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equals\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"待支付\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            state \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \"已取消\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  未支付直接关单，当前状态：已取消\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        } \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (state.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equals\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"已支付\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            state \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \"已取消\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  走退款流程，当前状态：已取消\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        } \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (state.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equals\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"已发货\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  已发货订单不能直接取消，请走退货流程\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        } \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (state.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equals\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"已取消\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  订单已取消，请勿重复操作\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> ship\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (state.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equals\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"待支付\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  未支付订单不能发货\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        } \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (state.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equals\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"已支付\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            state \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \"已发货\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  发货成功，当前状态：已发货\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        } \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (state.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equals\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"已发货\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  订单已发货，请勿重复发货\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        } \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (state.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equals\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"已取消\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">            System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  订单已取消，无法发货\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">    \u002F\u002F 状态是 String，流转规则全靠每个方法里的人肉 if-else 对齐：\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">    \u002F\u002F 新加一个\"已完成\"状态？三个方法全要改，十二个分支挨个检查，\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">    \u002F\u002F 漏掉任何一处，就会出现\"已取消的订单还在发货\"这种灵异事件\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>病灶拆解：三个方法 × 四个状态 = \u003Cstrong>十二个分支\u003C\u002Fstrong>，而且这个数字是乘法关系增长的——状态加到六个、动作加到五个，就是三十个分支的矩阵。更阴险的是，这个矩阵没有任何东西帮你对齐：某次需求改了\"已发货不能取消\"的文案，你只想改一处，却必须在三个方法里分别确认有没有散落的同款逻辑。\u003C\u002Fp>\n\u003Ch3 id=\"toc-6\">4.2 正解：每种状态一个类\u003C\u002Fh3>\n\u003Cp>第一步，抽象状态——把订单的所有动作定义为接口：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> interface\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> OrderState\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> pay\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(OrderContext \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">order\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> cancel\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(OrderContext \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">order\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> ship\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(OrderContext \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">order\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>第二步，上下文。注意它有多\"闲\"——三个动作全是原样转发：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> class\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> OrderContext\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    private\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> final\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> String orderId;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    private\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> OrderState state;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> OrderContext\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(String \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">orderId\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">        this\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">.orderId \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> orderId;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">        this\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">.state \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> UnpaidState\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  订单 \"\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> orderId \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">+\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \" 下单成功，当前状态：待支付\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> pay\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() { state.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">pay\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">this\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">); }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> cancel\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() { state.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">cancel\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">this\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">); }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> ship\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() { state.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">ship\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">this\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">); }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> setState\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(OrderState \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">state\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) { \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">this\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">.state \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> state; }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> String \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getOrderId\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() { \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">return\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> orderId; }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>第三步，具体状态。待支付状态：支付放行、取消放行、发货拒绝——流转规则写在状态自己家里：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> class\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> UnpaidState\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> implements\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> OrderState\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    @\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">Override\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> pay\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(OrderContext \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">order\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        order.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">setState\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> PaidState\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">());\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  [待支付] 支付成功，流转：待支付 → 已支付\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    @\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">Override\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> cancel\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(OrderContext \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">order\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        order.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">setState\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> CancelledState\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">());\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  [待支付] 未支付直接关单，流转：待支付 → 已取消\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    @\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">Override\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> ship\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(OrderContext \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">order\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"  [待支付] 未支付订单不能发货\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>已支付（重复支付拒绝、取消走退款、发货放行）、已发货（取消只能走退货流程）、已取消（全部拒绝）三个状态类结构完全相同，各自安放各自的规则。\u003C\u002Fp>\n\u003Cp>最后测试——重点看两笔订单的对照：订单 A 一路走到已发货再取消，订单 B 下单就反悔：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> class\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> StateTest\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> static\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> main\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">String\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">[] \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">args\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"========== 反面教材：if-else 状态机 ==========\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        BadOrder bad \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> BadOrder\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        bad.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">pay\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        bad.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">pay\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        bad.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">ship\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        bad.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">cancel\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"========== 正解：状态模式 ==========\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"-- 订单A：一路走下去，再想取消 --\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        OrderContext orderA \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> OrderContext\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"A-2024-001\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        orderA.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">pay\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        orderA.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">ship\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        orderA.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">cancel\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"-- 订单B：下单就反悔 --\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        OrderContext orderB \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> OrderContext\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"B-2024-002\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        orderB.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">cancel\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        orderB.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">pay\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        orderB.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">ship\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>运行输出：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan>========== 反面教材：if-else 状态机 ==========\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  支付成功，当前状态：已支付\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  该订单已支付，请勿重复支付\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  发货成功，当前状态：已发货\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  已发货订单不能直接取消，请走退货流程\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>========== 正解：状态模式 ==========\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>-- 订单A：一路走下去，再想取消 --\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  订单 A-2024-001 下单成功，当前状态：待支付\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  [待支付] 支付成功，流转：待支付 → 已支付\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  [已支付] 发货成功，流转：已支付 → 已发货\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  [已发货] 已发货不能直接取消，请走退货流程\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>-- 订单B：下单就反悔 --\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  订单 B-2024-002 下单成功，当前状态：待支付\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  [待支付] 未支付直接关单，流转：待支付 → 已取消\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  [已取消] 订单已取消，无法支付\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  [已取消] 订单已取消，无法发货\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"toc-7\">4.3 三处细节咂摸一下\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>细节一：流转规则搬家了。\u003C\u002Fstrong>\u003Ccode>order.setState(new PaidState())\u003C\u002Fcode> 写在 UnpaidState 内部——\"我支付之后去哪\"由我自己决定，不再由上下文的某个 if 分支决定。每个状态类就是流转图上的一个节点，节点只画自己出边的箭头。想看完整流转图？把每个状态类的方法扫一遍，出边就全齐了。\u003C\u002Fp>\n\u003Cp>**细节二：同一个 cancel()，三种命运。**订单 A 在已发货状态取消，被引导去退货流程；订单 B 在待支付状态取消，直接关单；若在已支付状态，则会走退款。客户端三次调用的代码一模一样，行为差异完全由\"当前是谁在值班\"决定——这就是\"状态驱动行为\"，也是定义里\"对象看起来好像修改了它的类\"的真身：同一个 OrderContext，状态一换，整个行为模式全变。\u003C\u002Fp>\n\u003Cp>**细节三：上下文被架空成转发器，这是设计而不是偷懒。**OrderContext 的三个方法清一色 \u003Ccode>state.xxx(this)\u003C\u002Fcode>——它存在的意义只剩\"持有当前状态 + 提供身份信息\"。有人嫌这层委托多余想砍掉，砍了之后调用方就得自己持有状态对象、自己管流转，状态模式的封装就破了。上下文是外界和状态机之间的防火墙。\u003C\u002Fp>\n\u003Ch3 id=\"toc-8\">4.4 开闭原则的成色\u003C\u002Fh3>\n\u003Cp>诚实地说：状态模式对开闭原则的兑现是\"打折扣\"的。新加\"已完成\"状态，确实不用改 if-else 矩阵了，但 ShippedState 的收货动作、CancelledState……凡是流转图上有边指向新状态的旧状态类，都得动。\u003C\u002Fp>\n\u003Cp>它的真实收益是：**把\"改\"的范围从\"全方法全分支\"压缩到\"相邻状态类\"。**改动的爆炸半径从 O(方法数 × 状态数) 缩到 O(相邻状态数)——这就是那张流转图的意义：图越长越密，状态模式越划算；三两个状态的小图，enum 加 switch 反而是更好的工程选择。\u003C\u002Fp>\n\u003Ch2 id=\"toc-9\">五、在框架里找它\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Thread——JDK 亲手实现的状态机。\u003C\u002Fstrong>\u003Ccode>Thread.State\u003C\u002Fcode> 枚举：NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED，六种状态一张流转图。而你早就体验过\"状态驱动行为\"：\u003Ccode>start()\u003C\u002Fcode> 只在 NEW 状态合法，对同一个线程调第二次 start 会抛 \u003Ccode>IllegalThreadStateException\u003C\u002Fcode>——\"已启动的线程不能再启动\"，这正是 CancelledState 拦截 ship 的同款逻辑。顺带注意 \u003Ccode>Thread.State\u003C\u002Fcode> 是 enum：无字段的状态类用枚举实现，天然单例（② 单例模式的老朋友），本文四个状态类同样无字段，工程上完全可以枚举化。\u003C\u002Fp>\n\u003Cp>**Spring StateMachine。**Spring 官方状态机框架：Builder 里声明状态集合、事件集合、流转规则、流转守卫（条件放行）、动作监听器。订单、支付网关、物联网设备状态，状态超过十个、流转带条件时，它比手写状态类省心得多——本篇手写状态类是为看懂骨架，生产里复杂场景请直接上框架。\u003C\u002Fp>\n\u003Cp>**工作流引擎。**Activiti、Flowable、Camunda 把\"状态+流转\"做成了数据库表 + 流程图 DSL：审批流的每个节点是一个状态，审批通过是流转事件，会签、驳回、超时自动流转都是引擎内置能力。企业级审批系统的终极形态。\u003C\u002Fp>\n\u003Cp>**TCP 协议栈。**三次握手、四次挥手那张经典状态图（CLOSED→SYN_SENT→ESTABLISHED→FIN_WAIT……），就是操作系统内核里一台真实运转的状态机——状态模式的威力，撑得起整个互联网的连接管理。\u003C\u002Fp>\n\u003Cp>一句话总结这一节：\u003Cstrong>凡是\"行为随状态变、状态按图流转\"的地方，从 JDK 到内核到工作流，状态机无处不在；状态模式就是状态机的面向对象实现。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Ch2 id=\"toc-10\">六、状态 vs 策略：孪生兄弟的最后一战\u003C\u002Fh2>\n\u003Cp>⑱ 讲命令 vs 策略时留了个尾巴：状态模式和策略模式结构也几乎一样——接口、一堆实现类、一个持有者。这两个是 GoF 二十三式里最著名的孪生兄弟，系列走到这里，最后一战做个了断：\u003C\u002Fp>\n\u003Ctable>\n \u003Cthead>\n  \u003Ctr>\n   \u003Cth>\u003C\u002Fth>\n   \u003Cth>策略模式 ⑮\u003C\u002Fth>\n   \u003Cth>状态模式 ⑲\u003C\u002Fth>\n  \u003C\u002Ftr>\n \u003C\u002Fthead>\n \u003Ctbody>\n  \u003Ctr>\n   \u003Ctd>一堆实现类是什么\u003C\u002Ftd>\n   \u003Ctd>平行的**算法**\u003C\u002Ftd>\n   \u003Ctd>流转图上的**节点**\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>实现类之间认识吗\u003C\u002Ftd>\n   \u003Ctd>互不相识\u003C\u002Ftd>\n   \u003Ctd>相识（知道流转目标是谁）\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>谁来切换\u003C\u002Ftd>\n   \u003Ctd>**外部**指定（调用方\u002F工厂）\u003C\u002Ftd>\n   \u003Ctd>**内部**流转（状态类自己 setState）\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>切换像什么\u003C\u002Ftd>\n   \u003Ctd>换插槽上的卡\u003C\u002Ftd>\n   \u003Ctd>图上的节点跳转\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>关心历史吗\u003C\u002Ftd>\n   \u003Ctd>不关心\u003C\u002Ftd>\n   \u003Ctd>当前状态就是全部历史沉淀\u003C\u002Ftd>\n  \u003C\u002Ftr>\n \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>最狠的分辨方法来了：**把 UnpaidState 里那句 \u003Ccode>order.setState(new PaidState())\u003C\u002Fcode> 删掉，状态模式瞬间退化成策略模式。**分水岭就一句话——\u003Cstrong>有没有流转\u003C\u002Fstrong>。策略是一排平行选项，选哪个、何时换，外面的世界说了算；状态是一张有向图，走哪条边，图上的规则说了算。\u003C\u002Fp>\n\u003Cp>面试官问\"状态和策略有啥区别\"，先答这张表，再补一句\"删除 setState 调用、状态模式就变成策略模式\"，基本稳了。\u003C\u002Fp>\n\u003Ch2 id=\"toc-11\">写在最后\u003C\u002Fh2>\n\u003Cp>状态模式一句话总结：\u003Cstrong>每种状态一个类，行为跟着状态走；流转规则住进状态自己家，非法操作在源头就被拦下。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>下一篇 ⑳ 迭代器模式——轻松一点的话题。你每天都在写 \u003Ccode>for (String s : list)\u003C\u002Fcode>，有没有想过这个冒号背后是谁在工作？为什么 ArrayList、HashSet、LinkedList 都能被 for-each，而你自己写的类就不行？怎么让你自己的集合也享受同款待遇？\u003Cstrong>统一的遍历协议——迭代器模式，下一篇见。\u003C\u002Fstrong>\u003C\u002Fp>",[112,115,118,121,124,128,131,134,137,140,143],{"id":113,"text":114,"level":107},"toc-1","一、状态模式是什么",{"id":116,"text":117,"level":107},"toc-2","二、优点与缺点",{"id":119,"text":120,"level":107},"toc-3","三、什么时候用",{"id":122,"text":123,"level":107},"toc-4","四、实现方式：订单状态机",{"id":125,"text":126,"level":127},"toc-5","4.1 反面教材：if-else 状态机",3,{"id":129,"text":130,"level":127},"toc-6","4.2 正解：每种状态一个类",{"id":132,"text":133,"level":127},"toc-7","4.3 三处细节咂摸一下",{"id":135,"text":136,"level":127},"toc-8","4.4 开闭原则的成色",{"id":138,"text":139,"level":107},"toc-9","五、在框架里找它",{"id":141,"text":142,"level":107},"toc-10","六、状态 vs 策略：孪生兄弟的最后一战",{"id":144,"text":145,"level":107},"toc-11","写在最后"]