跳至正文
来两杯美式
返回

设计模式之状态模式:状态变,行为变

By 来两杯美式
发布于

这是 2020 年 8 月整理的设计模式专题第十四篇。状态模式是行为型模式里较难的一篇——笔记只留下了简短定义,本文补全了完整示例。它的核心是:对象的行为由内部状态决定,状态一变,行为就变

简单说明

状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为

传统状态机(if-else 堆砌):          状态模式:
if (state == 待支付) {                 状态接口 <── 待支付状态
    行为A                              ├── 已支付状态
} else if (state == 已支付) {           └── 已发货状态
    行为B                                  (每个状态是一个类,
} else if (state == 已发货) {                  状态迁移由状态类自己驱动)
    行为C
}

为什么需要状态模式

订单的状态流转是典型场景:待支付 → 已支付 → 已发货 → 已完成。用 if-else 写:

public void handle(Order order) {
    if ("待支付".equals(order.getState())) {
        // 待支付:可以支付
    } else if ("已支付".equals(order.getState())) {
        // 已支付:可以发货
    } else if ("已发货".equals(order.getState())) {
        // 已发货:可以确认收货
    }
    // 每加一个状态,都要改这个方法 —— 违反开闭原则
}

问题:所有状态的行为堆在一个方法里,新增状态就要改这段逻辑,且状态越多方法越臃肿。状态模式把”每个状态的行为”抽成独立的类。

代码实现

以订单状态流转为例:

/**
 * 描述:订单上下文(持有当前状态)
 */
public class Order {
    private OrderState state = new PendingPayState();   // 初始:待支付

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

    public void pay() {
        state.pay(this);       // 行为委托给当前状态对象
    }

    public void ship() {
        state.ship(this);
    }

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

/**
 * 描述:状态接口:定义每种状态下各操作的行为
 */
public interface OrderState {
    void pay(Order order);
    void ship(Order order);
    void confirm(Order order);
}

/**
 * 描述:待支付状态
 */
public class PendingPayState implements OrderState {
    @Override
    public void pay(Order order) {
        System.out.println("支付成功,订单变为已支付");
        order.setState(new PaidState());    // 状态迁移:自己驱动转换
    }

    @Override
    public void ship(Order order) {
        System.out.println("未支付不能发货");
    }

    @Override
    public void confirm(Order order) {
        System.out.println("未支付不能确认收货");
    }
}

/**
 * 描述:已支付状态
 */
public class PaidState implements OrderState {
    @Override
    public void pay(Order order) {
        System.out.println("已支付,请勿重复支付");
    }

    @Override
    public void ship(Order order) {
        System.out.println("发货成功,订单变为已发货");
        order.setState(new ShippedState());
    }

    @Override
    public void confirm(Order order) {
        System.out.println("未发货不能确认收货");
    }
}

/**
 * 描述:已发货状态
 */
public class ShippedState implements OrderState {
    @Override
    public void pay(Order order) {
        System.out.println("已发货,无需支付");
    }

    @Override
    public void ship(Order order) {
        System.out.println("已发货,请勿重复发货");
    }

    @Override
    public void confirm(Order order) {
        System.out.println("确认收货,订单完成");
        order.setState(new FinishedState());
    }
}

/**
 * 描述:已完成状态(终态)
 */
public class FinishedState implements OrderState {
    @Override
    public void pay(Order order) {
        System.out.println("订单已完成");
    }

    @Override
    public void ship(Order order) {
        System.out.println("订单已完成");
    }

    @Override
    public void confirm(Order order) {
        System.out.println("订单已完成");
    }
}

public class StateTest {
    public static void main(String[] args) {
        Order order = new Order();
        order.pay();      // 待支付状态下调用 → 支付成功,转为已支付
        order.ship();     // 已支付状态下调用 → 发货成功,转为已发货
        order.confirm();  // 已发货状态下调用 → 确认收货,完成
        order.pay();      // 已完成状态下调用 → "订单已完成"(非法操作被状态拦截)
    }
}

关键点

  1. Context(Order)持有状态对象,所有操作委托给当前状态;
  2. 每个状态一个类pay/ship/confirm 在各自状态类里实现”这个状态下该怎么做”;
  3. 状态迁移由状态类自己驱动order.setState(new PaidState()) —— 合法操作让状态前进,非法操作被拦截并提示;
  4. 新增状态:加一个状态类 + 调整迁移逻辑,不用改所有 if-else。

状态模式 vs 策略模式

两者结构相似(都是”上下文 + 接口 + 多个实现类”),但意图完全不同

对比项状态模式策略模式
关注点对象内部状态的变化驱动行为变化同一业务选择不同算法
状态切换状态之间自动流转(状态类自己驱动)策略由客户端主动选择,互不相识
行为同一个操作在不同状态下行为不同不同策略实现同一接口的不同算法
类比人有情绪,心情不同反应不同出行选公交/打车,算法不同目标相同

一句话区分:策略模式是”换算法”,状态模式是”换身份”——状态模式里对象当前的行为完全由它处于哪个状态决定。

适用场景与优缺点

适用场景

优点

缺点

现实中的应用

场景说明
订单状态机待支付→已支付→已发货→已完成
TCP 连接状态LISTEN→SYN_SENT→ESTABLISHED→CLOSE_WAIT
工作流引擎审批状态流转
播放器状态播放/暂停/停止,同一按键在不同状态下行为不同

小结


分享这篇文章:
通过邮件分享这篇文章✓ 链接已复制
查看系列全部文章
  1. 01.软件设计原则:SOLID + KISS / YAGNI / LOD 全景
  2. 02.设计模式之原型模式,及深浅拷贝
  3. 03.设计模式之工厂模式:简单工厂、工厂方法与抽象工厂
  4. 04.设计模式之建造者模式:复杂对象的组装艺术
  5. 05.设计模式之适配器模式:让不兼容的接口协同工作
  6. 06.设计模式之桥接模式:抽象与实现分离,各自独立变化
  7. 07.设计模式之装饰者模式:动态增强,比继承更有弹性
  8. 08.设计模式之代理模式:静态代理与 JDK 动态代理
  9. 09.设计模式之外观模式:统一门面,简化子系统调用
  10. 10.设计模式之享元模式:共享细粒度对象,降低内存占用
  11. 11.设计模式之策略模式:算法家族,自由切换
  12. 12.设计模式之模板方法模式:固定骨架,可变步骤
  13. 13.设计模式之观察者模式:一对多依赖,状态变更自动通知
  14. 14.设计模式之责任链模式:请求逐级传递,动态组合处理者
  15. 15.设计模式之状态模式:状态变,行为变
  16. 16.设计模式之迭代器模式:不暴露内部实现,顺序访问集合

上一篇
设计模式之迭代器模式:不暴露内部实现,顺序访问集合
下一篇
Docker 集群演进史:Swarm → Compose → Kubernetes