Java设计模式-状态模式

发布于:2025-08-19 ⋅ 阅读:(14) ⋅ 点赞:(0)

Java设计模式-状态模式

模式概述

状态模式简介

核心思想:允许对象在内部状态改变时改变其行为,使对象看起来好像修改了其类。本质是将状态相关的行为封装到独立的状态类中,通过状态切换动态改变对象的行为逻辑。

模式类型:行为型设计模式(关注对象间的交互与职责分配)。

作用

  • 将状态相关的行为局部化,每个状态类独立管理自身行为,避免代码冗余;
  • 使状态转换显式化,通过状态对象间的协作替代复杂的条件判断;
  • 符合开闭原则,新增状态只需添加新状态类,无需修改现有代码。

典型应用场景

  • 订单状态流转(待支付→已支付→已发货→已完成→售后中);
  • 交通灯状态切换(红灯→绿灯→黄灯);
  • 工作流引擎中的步骤处理(草稿→审核中→已发布→已归档);
  • 游戏角色状态(正常→中毒→隐身→死亡)。

我认为:状态模式是“行为的动态切换器”,通过将每个状态的行为独立成类,让对象在不同状态下像“换了一个人”一样工作,彻底告别冗长的if-else地狱。

课程目标

  • 理解状态模式的核心思想和经典应用场景
  • 识别应用场景,使用状态模式解决功能要求
  • 了解状态模式的优缺点

核心组件

角色-职责表

角色 职责 示例类名
抽象状态(State) 定义所有具体状态的公共接口,声明该状态下需要处理的行为方法 OrderState
具体状态(Concrete State) 实现抽象状态接口,封装该状态下的具体行为逻辑,负责状态转换(可选) UnpaidStatePaidState
上下文(Context) 持有当前状态对象,将外部请求委托给当前状态处理;提供状态切换方法 OrderContext

类图

下面是一个简化的类图表示,展示了状态模式中的主要角色及其交互方式:

持有
实现
实现
实现
实现空
Context
-OrderState currentState
+setState(OrderState state)
+pay()
+deliver()
+confirm()
«interface»
OrderState
+pay(Context context)
+deliver(Context context)
+confirm(Context context)
UnpaidState
+pay(Context context)
+deliver(Context context)
+confirm(Context context)
PaidState
+pay(Context context)
+deliver(Context context)
+confirm(Context context)
DeliveredState
+confirm(Context context)
+pay(Context context)
+deliver(Context context)
CompletedState
+pay(Context context)
+deliver(Context context)
+confirm(Context context)

传统实现 VS 状态模式

案例需求

案例背景:实现一个订单系统,订单有“待支付”“已支付”“已发货”三种状态,不同状态下执行不同操作(如支付、发货、确认收货)。

传统实现(痛点版)

代码实现

// 传统写法:上下文类直接包含状态判断逻辑
public class Order {
    private String state; // 状态:"未支付" "已支付" "已发货" "已完成"

    public void pay() {
        if ("未支付".equals(state)) {
            System.out.println("支付成功,订单状态变更为已支付");
            state = "已支付";
        } else {
            System.out.println("当前状态无法支付");
        }
    }

    public void deliver() {
        if ("已支付".equals(state)) {
            System.out.println("订单已发货,状态变更为已发货");
            state = "已发货";
        } else {
            System.out.println("当前状态无法发货");
        }
    }

    public void confirm() {
        if ("已发货".equals(state)) {
            System.out.println("确认收货,订单完成");
            state = "已完成";
        } else {
            System.out.println("当前状态无法确认收货");
        }
    }

    // 状态设置方法(省略getter/setter)
}

痛点总结

  • 代码臃肿:每个操作方法(pay/deliver/confirm)都需包含状态判断逻辑,状态越多代码越膨胀;
  • 可维护性差:新增状态(如“退款中”)需修改所有相关方法,违反开闭原则;
  • 状态耦合:状态转换逻辑分散在各方法中,难以统一管理和追踪;
  • 复用性低:状态相关行为无法被其他对象复用(如另一个订单类型需要相同状态逻辑)。

状态模式 实现(优雅版)

代码实现

// 1. 抽象状态接口
interface OrderState {
    void pay(OrderContext context);    // 支付操作
    void deliver(OrderContext context);// 发货操作
    void confirm(OrderContext context);// 确认收货
}

// 2. 具体状态类:未支付
class UnpaidState implements OrderState {
    @Override
    public void pay(OrderContext context) {
        System.out.println("支付成功,订单状态变更为已支付");
        context.setState(new PaidState()); // 切换状态
    }

    @Override
    public void deliver(OrderContext context) {
        System.out.println("当前状态未支付,无法发货");
    }

    @Override
    public void confirm(OrderContext context) {
        System.out.println("当前状态未支付,无法确认收货");
    }
}

// 3. 具体状态类:已支付
class PaidState implements OrderState {
    @Override
    public void pay(OrderContext context) {
        System.out.println("当前状态已支付,无需重复支付");
    }

    @Override
    public void deliver(OrderContext context) {
        System.out.println("订单已发货,状态变更为已发货");
        context.setState(new DeliveredState()); // 切换状态
    }

    @Override
    public void confirm(OrderContext context) {
        System.out.println("当前状态已支付,未发货,无法确认收货");
    }
}

// 4. 具体状态类:已发货(类似实现,省略部分代码)
class DeliveredState implements OrderState {
    @Override
    public void confirm(OrderContext context) {
        System.out.println("确认收货,订单完成");
        context.setState(new CompletedState());
    }
    // 其他方法实现...
}

// 5. 上下文类:订单上下文
class OrderContext {
    private OrderState currentState;

    public OrderContext() {
        this.currentState = new UnpaidState(); // 初始状态为未支付
    }

    public void setState(OrderState state) {
        this.currentState = state;
    }

    // 委托请求给当前状态处理
    public void pay() {
        currentState.pay(this);
    }

    public void deliver() {
        currentState.deliver(this);
    }

    public void confirm() {
        currentState.confirm(this);
    }
}

// 使用示例
public class Client {
    public static void main(String[] args) {
        OrderContext order = new OrderContext();
        order.pay();      // 输出:支付成功,订单状态变更为已支付
        order.deliver();  // 输出:订单已发货,状态变更为已发货
        order.confirm();  // 输出:确认收货,订单完成
    }
}

优势

  • 高内聚低耦合:每个状态的行为封装在独立类中,修改或新增状态仅需调整对应类;
  • 符合开闭原则:新增状态(如“退款中”)只需添加新的RefundedState类,无需修改现有代码;
  • 状态转换清晰:状态切换逻辑集中在状态类或上下文类中,易于追踪和管理;
  • 行为复用性高:状态类可被多个上下文对象共享(如多个订单共享同一状态实例)。

局限

  • 类数量增加:若状态数量过多(如超过10种),会导致类膨胀,增加系统复杂度;
  • 初始成本高:简单场景(如状态少且行为简单)下,状态模式可能过度设计;
  • 状态共享需注意线程安全:若多个上下文共享同一状态实例,需考虑并发修改问题(可通过无状态状态类或同步机制解决)。

模式变体

  • 共享状态(Flyweight State):当多个上下文需要共享同一状态实例时(如无状态状态类),可将状态对象池化,减少内存占用;

  • 状态转换集中管理:将状态转换逻辑从状态类中抽离,由上下文或独立的“状态机”类统一管理(适合复杂状态转换规则);

  • 枚举状态实现:利用Java枚举的特性,将每个状态定义为枚举值并实现状态接口(代码更简洁,适合轻量级场景);

    enum OrderStateEnum implements OrderState {
        UNPAID {
            @Override
            public void pay(OrderContext context) { /* 实现 */ }
        },
        PAID {
            @Override
            public void deliver(OrderContext context) { /* 实现 */ }
        };
    }
    
  • 分层状态模式:将状态按层级划分(如基础状态+扩展状态),减少重复代码(适合状态间有公共行为的场景)。


最佳实践

建议 理由
状态数量≥3时优先使用 状态较少(如2种)时,传统条件判断更简单;状态≥3时,状态模式的优势显著
状态类尽量无状态 无状态的状态类可被所有上下文共享(单例),减少内存开销
状态转换逻辑统一管理 若转换规则复杂(如依赖上下文数据),建议在上下文类中集中处理,避免状态类过度耦合
避免状态持有上下文强引用 防止内存泄漏,可通过弱引用或让上下文传递自身(如方法参数传入this
为状态接口定义完整契约 明确每个状态必须实现的方法,避免运行时出现未处理的行为分支

一句话总结

状态模式通过将状态相关的行为封装到独立的状态类中,使对象在不同状态下动态切换行为逻辑,彻底解决了传统实现中条件判断冗余、可维护性差的问题,是管理复杂状态流转的优雅解决方案。

如果关注Java设计模式内容,可以查阅作者的其他Java设计模式系列文章。😊


网站公告

今日签到

点亮在社区的每一天
去签到