设计模式学习(23) 23-21 状态模式

文章目录

  • [0. 个人感悟](#0. 个人感悟)
  • [1. 概念](#1. 概念)
  • [2. 适配场景](#2. 适配场景)
    • [2.1 适合的场景](#2.1 适合的场景)
    • [2.2 常见场景举例](#2.2 常见场景举例)
  • [3. 实现方法](#3. 实现方法)
    • [3.1 实现思路](#3.1 实现思路)
    • [3.2 UML类图](#3.2 UML类图)
    • [3.3 代码示例](#3.3 代码示例)
  • [4. 优缺点](#4. 优缺点)
    • [4.1 优点](#4.1 优点)
    • [4.2 缺点](#4.2 缺点)

0. 个人感悟

  • 状态模式旨在将状态封装成独立的类,各个状态子类完成不同的行为,状态改变时切换到不同的子类
  • 我理解是将状态x行为,按照状态维度展开。对比传统的按行为展开,会更符合面向对象,代码会更清晰,少许多if-else判断
  • 实际工作中遇到的类似场景还比较多,比如常见的工作流程流转业务,对于不同的状态有不同的操作,业务也会复杂很多,比如权限控制等等
  • 状态的切换可以由状态类来切换,也可以上下文管理,建议实际工作中抽取出专门的类进行管理,降低耦合

1. 概念

英文定义 (《设计模式:可复用面向对象软件的基础》)

Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.

中文翻译

允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

理解

  • 状态模式是一种行为型设计模式,核心思想是将状态封装为独立的类
  • 将对象在不同状态下的行为委托给当前状态对象执行,而非在对象内部使用大量条件判断
  • 当对象状态改变时,实际上是通过切换到不同的状态类实例来改变行为
  • 符合"开闭原则"------新增状态无需修改现有代码,只需添加新的状态类
  • 状态转换逻辑可以分散在各个状态类中,也可以集中在上下文类中管理

2. 适配场景

2.1 适合的场景

  1. 对象行为随状态改变而改变:对象需要根据当前状态执行不同操作
  2. 复杂的条件分支逻辑:代码中包含大量与对象状态相关的条件语句(if-else或switch-case)
  3. 状态转换逻辑固定且明确:状态之间的转换有明确的规则和条件
  4. 状态数量相对稳定:状态数量不会频繁变化,避免因状态过多导致类爆炸

2.2 常见场景举例

  1. 订单系统:订单的待支付、已支付、已发货、已完成、已取消等状态
  2. 工作流引擎:审批流程中的草稿、待审批、已审批、已驳回等状态
  3. 游戏开发:游戏角色的站立、行走、奔跑、跳跃、攻击、受伤等状态
  4. 网络连接:TCP连接中的CLOSED、LISTEN、SYN_SENT、ESTABLISHED等状态
  5. UI组件:按钮的启用、禁用、悬停、按下等交互状态
  6. 交通信号灯:红灯、黄灯、绿灯的状态切换

3. 实现方法

3.1 实现思路

  1. 定义状态接口:声明所有状态类必须实现的方法,这些方法对应上下文类中的各种操作
  2. 实现具体状态类:为每个状态创建独立的类,实现状态接口,封装该状态下的具体行为
  3. 创建上下文类
    • 维护当前状态对象的引用
    • 将客户端请求委托给当前状态对象处理
    • 提供设置状态的方法(状态转换)
  4. 管理状态转换
    • 可以在上下文类中集中管理(策略模式风格)
    • 也可以在各个状态类中分散管理(状态模式典型风格)
  5. 客户端使用:客户端通过上下文类与状态机交互,无需关心当前具体状态

3.2 UML类图

角色说明

  • Context(上下文):定义客户端感兴趣的接口,维护一个ConcreteState子类的实例
  • State(状态接口):定义一个接口以封装与Context的一个特定状态相关的行为
  • ConcreteState(具体状态):每个子类实现一个与Context的一个状态相关的行为

3.3 代码示例

背景

以自动咖啡机为例

  • 正常流程行为有投币、选咖啡、制作咖啡、取咖啡,异常行为有补充咖啡豆
  • 正常状态有等待投币、等待选择咖啡、已选择咖啡、咖啡已制作完成,异常状态为咖啡豆不足

行为接口

java 复制代码
public interface ICoffeeMachineState {  
    /**  
     * @param machine 咖啡机上下文  
     * @description 投币  
     * @author bigHao  
     * @date 2026/1/28  
     **/    
     void insertCoin(CoffeeMachine machine);  
  
    /**  
     * @param machine 咖啡机上下文  
     * @description 选择咖啡  
     * @author bigHao  
     * @date 2026/1/28  
     **/    
     void selectCoffee(CoffeeMachine machine);  
  
    /**  
     * @param machine 咖啡机上下文  
     * @description 制作咖啡  
     * @author bigHao  
     * @date 2026/1/28  
     **/    
     void dispenseCoffee(CoffeeMachine machine);  
  
    /**  
     * @param machine 咖啡机上下文  
     * @description 取走coffee  
     * @author bigHao  
     * @date 2026/1/28  
     **/    
     void takeCoffee(CoffeeMachine machine);  
  
    /**  
     * @param machine 咖啡机上下文  
     * @param amount  豆子数量  
     * @description 补充豆子  
     * @author bigHao  
     * @date 2026/1/28  
     **/    
     void refill(CoffeeMachine machine, int amount);  
}

行为实现类族

java 复制代码
\\ 等待投币
public class WaitingForCoinState implements ICoffeeMachineState {  
    @Override  
    public void insertCoin(CoffeeMachine machine) {  
        System.out.println("硬币已投入,请选择咖啡");  
        machine.setState(new WaitingForSelectState());  
    }  
  
    @Override  
    public void selectCoffee(CoffeeMachine machine) {  
        System.out.println("请先投币");  
    }  
  
    @Override  
    public void dispenseCoffee(CoffeeMachine machine) {  
        System.out.println("请先投币并选择咖啡");  
    }  
  
    @Override  
    public void takeCoffee(CoffeeMachine machine) {  
        System.out.println("请先投币并选择咖啡");  
    }  
  
    @Override  
    public void refill(CoffeeMachine machine, int amount) {  
        machine.addCoffeeBeans(amount);  
        System.out.println("添加了 " + amount + " 克咖啡豆");  
    }  
}
\\ 等待选择coffee
public class WaitingForSelectState implements ICoffeeMachineState {  
    @Override  
    public void insertCoin(CoffeeMachine machine) {  
        System.out.println("硬币已投入,请选择咖啡");  
        machine.setState(new WaitingForSelectState());  
    }  
  
    @Override  
    public void selectCoffee(CoffeeMachine machine) {  
        System.out.println("咖啡已选择,请按制作按钮");  
        machine.setState(new CoffeeSelectedState());  
    }  
  
    @Override  
    public void dispenseCoffee(CoffeeMachine machine) {  
        System.out.println("硬币已投入,请选择咖啡");  
    }  
  
    @Override  
    public void takeCoffee(CoffeeMachine machine) {  
        System.out.println("硬币已投入,请选择咖啡");  
    }  
  
    @Override  
    public void refill(CoffeeMachine machine, int amount) {  
        machine.addCoffeeBeans(amount);  
        System.out.println("添加了 " + amount + " 克咖啡豆");  
    }  
}
\\ coffee已选择
public class CoffeeSelectedState implements ICoffeeMachineState {  
    @Override  
    public void insertCoin(CoffeeMachine machine) {  
        System.out.println("已投币,请按制作按钮");  
    }  
  
    @Override  
    public void selectCoffee(CoffeeMachine machine) {  
        System.out.println("咖啡已选择,请按制作按钮");  
    }  
  
    @Override  
    public void dispenseCoffee(CoffeeMachine machine) {  
        if (machine.getCoffeeBeans() >= 10) {  
            System.out.println("正在制作咖啡...");  
            machine.reduceCoffeeBeans(10);  
            machine.setState(new CoffeeReadyState());  
        } else {  
            System.out.println("咖啡豆不足,请联系工作人员");  
            machine.setState(new OutOfBeanState());  
        }  
    }  
  
    @Override  
    public void takeCoffee(CoffeeMachine machine) {  
        System.out.println("咖啡已选择,请按制作按钮");  
    }  
  
    @Override  
    public void refill(CoffeeMachine machine, int amount) {  
        machine.addCoffeeBeans(amount);  
        System.out.println("添加了 " + amount + " 克咖啡豆");  
    }  
}
\\ coffee已制作
public class CoffeeReadyState implements ICoffeeMachineState {  
    @Override  
    public void insertCoin(CoffeeMachine machine) {  
        System.out.println("请取走您的咖啡后再投币");  
    }  
  
    @Override  
    public void selectCoffee(CoffeeMachine machine) {  
        System.out.println("请取走您的咖啡后再选择");  
    }  
  
    @Override  
    public void dispenseCoffee(CoffeeMachine machine) {  
        System.out.println("咖啡已准备好,请取走");  
    }  
  
    @Override  
    public void takeCoffee(CoffeeMachine machine) {  
        System.out.println("取走coffee,谢谢惠顾");  
        machine.setState(new WaitingForCoinState());  
    }  
  
    @Override  
    public void refill(CoffeeMachine machine, int amount) {  
        machine.addCoffeeBeans(amount);  
        System.out.println("添加了 " + amount + " 克咖啡豆");  
    }  
}
\\ 咖啡豆不足
public class OutOfBeanState implements ICoffeeMachineState {  
    @Override  
    public void insertCoin(CoffeeMachine machine) {  
        System.out.println("咖啡豆不足,无法制作咖啡");  
    }  
  
    @Override  
    public void selectCoffee(CoffeeMachine machine) {  
        System.out.println("咖啡豆不足,无法制作咖啡");  
    }  
  
    @Override  
    public void dispenseCoffee(CoffeeMachine machine) {  
        System.out.println("咖啡豆不足,无法制作咖啡");  
    }  
  
    @Override  
    public void takeCoffee(CoffeeMachine machine) {  
        System.out.println("咖啡豆不足,无法制作咖啡");  
    }  
  
    @Override  
    public void refill(CoffeeMachine machine, int amount) {  
        machine.addCoffeeBeans(amount);  
        System.out.println("添加了 " + amount + " 克咖啡豆");  
        if (machine.getCoffeeBeans() >= 10) {  
            machine.setState(new CoffeeSelectedState());  
        }  
    }  
}

测试

java 复制代码
public class Client {  
    static void main() {  
        // 正常流程  
        System.out.println("=======正常流程示例=======");  
        CoffeeMachine coffeeMachine = new CoffeeMachine(11);  
        printState(coffeeMachine);  
  
        System.out.println("===投币===");  
        coffeeMachine.insertCoin();  
        printState(coffeeMachine);  
  
        System.out.println("===选择coffee===");  
        coffeeMachine.selectCoffee();  
        printState(coffeeMachine);  
  
        System.out.println("===制作coffee===");  
        coffeeMachine.dispenseCoffee();  
        printState(coffeeMachine);  
  
        System.out.println("===取走coffee===");  
        coffeeMachine.takeCoffee();  
        printState(coffeeMachine);  
  
        System.out.println("=======豆子不足流程示例=======");  
        // 异常情况 咖啡豆不足  
        coffeeMachine.insertCoin();  
        printState(coffeeMachine);  
  
        coffeeMachine.selectCoffee();  
        printState(coffeeMachine);  
  
        System.out.println("===咖啡豆不足===");  
        coffeeMachine.dispenseCoffee();  
        printState(coffeeMachine);  
  
        // 补充咖啡豆  
        System.out.println("===补充coffee===");  
        coffeeMachine.refill(100);  
        printState(coffeeMachine);  
  
        System.out.println("===制作coffee===");  
        coffeeMachine.dispenseCoffee();  
  
        printState(coffeeMachine);  
        System.out.println("===取走coffee===");  
        coffeeMachine.takeCoffee();  
        printState(coffeeMachine);  
  
    }  
  
    public static void printState(CoffeeMachine coffeeMachine) {  
        System.out.println(STR."当前状态: \{coffeeMachine.getCurrentState()}");  
    }  
  
}

输出

复制代码
=======正常流程示例=======
当前状态: WaitingForCoinState
===投币===
硬币已投入,请选择咖啡
当前状态: WaitingForSelectState
===选择coffee===
咖啡已选择,请按制作按钮
当前状态: CoffeeSelectedState
===制作coffee===
正在制作咖啡...
当前状态: CoffeeReadyState
===取走coffee===
取走coffee,谢谢惠顾
当前状态: WaitingForCoinState
=======豆子不足流程示例=======
硬币已投入,请选择咖啡
当前状态: WaitingForSelectState
咖啡已选择,请按制作按钮
当前状态: CoffeeSelectedState
===咖啡豆不足===
咖啡豆不足,请联系工作人员
当前状态: OutOfBeanState
===补充coffee===
添加了 100 克咖啡豆
当前状态: CoffeeSelectedState
===制作coffee===
正在制作咖啡...
当前状态: CoffeeReadyState
===取走coffee===
取走coffee,谢谢惠顾
当前状态: WaitingForCoinState

4. 优缺点

4.1 优点

  1. 单一职责原则:将与特定状态相关的代码放在独立的类中
  2. 开闭原则:新增状态无需修改现有状态类或上下文类
  3. 消除复杂条件判断:避免使用大量的if-else或switch-case语句
  4. 提高可读性:状态转换逻辑清晰,代码结构更易理解
  5. 状态管理集中:所有状态相关的代码集中在状态类中,便于维护

4.2 缺点

  1. 类数量增加:每个状态都需要一个类,状态过多会导致类爆炸
  2. 状态转换逻辑分散:如果转换逻辑在各状态类中,可能导致逻辑分散难以跟踪
  3. 上下文与状态耦合:状态类需要了解上下文类的接口,增加了耦合度
  4. 性能开销:状态切换涉及对象创建和销毁,可能带来额外开销

参考:

相关推荐
半桔2 小时前
【设计模式】策略模式:可插拔算法,从硬编码到灵活适配,体会“算法解耦“思想
java·c++·算法·设计模式·策略模式
QiZhang | UESTC2 小时前
学习日记day71
学习
ddxu2 小时前
AI学习笔记
笔记·学习·ai
肥硕之虎2 小时前
渗透高级课个人学习分享
学习
2601_949720262 小时前
flutter_for_openharmony手语学习app实战+学习进度实现
javascript·学习·flutter
楼田莉子2 小时前
Linux进程间通信——System V系列
linux·服务器·c++·学习·信息与通信
321.。2 小时前
从 0 到 1 实现 Linux 下的线程安全阻塞队列:基于 RAII 与条件变量
linux·开发语言·c++·学习·中间件
啵啵鱼爱吃小猫咪2 小时前
机器人标准DH(SDH)与改进DH(MDH)
开发语言·人工智能·python·学习·算法·机器人
钦拆大仁2 小时前
Java设计模式-中介者模式
设计模式·中介者模式