Java状态模式源码剖析及使用场景

状态模式

一、介绍

状态模式(State Pattern)是一种行为型设计模式,它允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

状态模式的核心思想是将对象的状态封装成独立的类,使得对象可以根据自身的状态来获取不同的行为。当对象的内部状态发生改变时,其行为也会随之改变。

状态模式的主要角色有:

  1. Context(上下文): 定义客户感兴趣的接口,并维护一个具体状态类的实例。

  2. State(状态): 这是一个抽象状态类,它定义了一个与Context的一个或多个行为相关的接口。

  3. ConcreteState(具体状态): 实现了抽象状态类所对应的行为。每一个子类实现对应于Context的一种状态。

状态模式的优点:

  1. 解耦了状态和行为,将它们分布到不同的类中,使得状态的改变不会影响行为的改变,行为的改变也不会影响状态的改变。
  2. 每种状态都可以根据自身的特点执行业务逻辑,因为每种状态对应一个子类。
  3. 支持对象状态的切换。
  4. 方便进行扩展,由于状态和行为是分开的,所以可以很方便地添加新的状态和行为。

状态模式的缺点:

  1. 会导致系统中类的个数增多。
  2. 在使用的过程中,需要分清系统中的每一种状态所对应的行为,以免产生错误。

状态模式的应用场景:

  1. 行为随状态改变而改变的场景。
  2. 一个操作中含有大量分支语句时,可以使用状态模式来把相关的分支语句转移到各个状态中。
  3. 处理订单流程、用户状态管理等领域都可以使用状态模式。

二、订单处理系统中使用

需求:一个简单的订单处理系统,订单从下单开始,经过多个状态的转换,最终到达完成状态。订单可能存在的状态包括:已下单(OrderedState)、已付款(PaidState)、已发货(ShippedState)、已签收(ReceivedState)、已完成(CompletedState)。当订单处于不同状态时,对应的操作也不同,比如在已下单状态下可以支付订单,在已付款状态下可以发货,以此类推。

java 复制代码
// 订单状态接口,定义订单可执行的操作,如支付、发货、签收、完成等
interface OrderState {
    void pay();
    void ship();
    void receive();
    void complete();
}

// 已下单状态,实现了订单OrderState在不同状态下的具体行为。
class OrderedState implements OrderState {
    private Order order;

    public OrderedState(Order order) {
        this.order = order;
    }

    @Override
    public void pay() {
        System.out.println("已支付订单 " + order.getOrderId());
        order.setState(new PaidState(order));
    }

    @Override
    public void ship() {
        System.out.println("订单尚未支付,无法发货");
    }

    @Override
    public void receive() {
        System.out.println("订单尚未发货,无法签收");
    }

    @Override
    public void complete() {
        System.out.println("订单尚未发货,无法完成");
    }
}

// 已付款状态
class PaidState implements OrderState {
    private Order order;

    public PaidState(Order order) {
        this.order = order;
    }

    @Override
    public void pay() {
        System.out.println("订单已经支付,无需重复支付");
    }

    @Override
    public void ship() {
        System.out.println("正在发货订单 " + order.getOrderId());
        order.setState(new ShippedState(order));
    }

    @Override
    public void receive() {
        System.out.println("订单尚未发货,无法签收");
    }

    @Override
    public void complete() {
        System.out.println("订单尚未发货,无法完成");
    }
}

// 其他状态类实现...

// 订单上下文,它维护了订单的当前状态,并提供了相应的操作方法。这些方法内部会委托给当前状态对象执行具体的操作。
class Order {
    private String orderId;
    private OrderState state;

    public Order(String orderId) {
        this.orderId = orderId;
        this.state = new OrderedState(this); // 初始状态为已下单
    }

    public void pay() {
        state.pay();
    }

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

    public void receive() {
        state.receive();
    }

    public void complete() {
        state.complete();
    }

    public String getOrderId() {
        return orderId;
    }
	
	//改变订单的当前状态
    public void setState(OrderState state) {
        this.state = state;
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Order order = new Order("1001");

        order.pay(); // 已支付订单 1001
        order.ship(); // 正在发货订单 1001
        order.receive(); // 已签收订单 1001
        order.complete(); // 订单已完成
    }
}
  1. OrderState 接口定义了订单可执行的操作,如支付、发货、签收、完成等。
  2. OrderedStatePaidStateShippedStateReceivedStateCompletedState 分别实现了订单在不同状态下的具体行为。
  3. Order 类是状态模式的上下文,它维护了订单的当前状态,并提供了相应的操作方法,如 pay()ship() 等。这些方法内部会委托给当前状态对象执行具体的操作。
  4. Order 类的 setState() 方法用于改变订单的当前状态。
  5. 在客户端代码中,我们创建了一个订单对象,然后依次调用支付、发货、签收、完成操作,订单状态会随着操作的执行而发生改变。

通过状态模式的实现,将订单的不同状态和行为分离开来,使得订单在状态改变时可以自动切换行为,提高了代码的可扩展性和可维护性。同时,由于每个状态对应一个子类,我们也可以很方便地添加新的状态和行为,满足未来需求的变化。

三、Spring 中事务怎么使用状态模式?

在 Spring 事务管理中,TransactionDefinition 接口定义了事务的各种属性,比如传播行为、隔离级别、超时时间等。而 TransactionStatus 接口则表示事务的不同状态,它是一个标记接口,具体的状态由其实现类 DefaultTransactionStatus 来维护。

DefaultTransactionStatus 内部使用一个 State 枚举来表示事务的不同状态,包括以下几种:

java 复制代码
private enum State {
    ACTIVE,
    COMMITTED,
    ROLLED_BACK,
    COMMITTED_AFTER_ROLLBACK,
    ROLLBACK_AFTER_COMMITTED
}
  • ACTIVE: 事务处于活动状态
  • COMMITTED: 事务已提交
  • ROLLED_BACK: 事务已回滚
  • COMMITTED_AFTER_ROLLBACK: 事务在回滚后又被提交
  • ROLLBACK_AFTER_COMMITTED: 事务在提交后又被回滚

DefaultTransactionStatus 实现了 TransactionStatus 接口中的方法,根据当前的状态执行相应的操作。例如 setRollbackOnly() 方法用于标记事务需要回滚

java 复制代码
public void setRollbackOnly() {
    switch (this.state) {
        case ACTIVE:
            setState(State.ROLLED_BACK);
            break;
        case COMMITTED:
            setState(State.ROLLBACK_AFTER_COMMITTED);
            break;
        case ROLLED_BACK:
            // Do nothing
            break;
        case COMMITTED_AFTER_ROLLBACK:
            setState(State.ROLLBACK_AFTER_COMMITTED);
            break;
        case ROLLBACK_AFTER_COMMITTED:
            // Do nothing
            break;
        default:
            throw new IllegalStateException("Invalid state transition attempted");
    }
}

这段代码根据当前事务的状态执行不同的操作。如果事务处于 ACTIVE 状态,则将其标记为 ROLLED_BACK。如果事务已经提交,则将其标记为 ROLLBACK_AFTER_COMMITTED 等。

另外,Spring 还提供了 TransactionSynchronization 接口,用于在事务生命周期的不同阶段执行特定的逻辑。AbstractPlatformTransactionManager 类中的 invokeAfterCommitinvokeAfterRollback 方法就会根据事务的当前状态决定是否调用相应的同步回调方法

java 复制代码
protected void invokeAfterCommit(DefaultTransactionStatus status) {
    if (status.getState() == DefaultTransactionStatus.State.COMMITTED_AFTER_ROLLBACK) {
        invokeAfterRollbackHandlers(status);
    }
    invokeAfterCommitHandlers(status);
}

protected void invokeAfterRollback(DefaultTransactionStatus status) {
    if (status.getState() == DefaultTransactionStatus.State.ROLLBACK_AFTER_COMMITTED) {
        invokeAfterCommitHandlers(status);
    }
    invokeAfterRollbackHandlers(status);
}

这些方法会根据事务的状态决定是先执行 commit 回调还是 rollback 回调,或者两者都执行。

看到 Spring 在处理事务管理时,使用了状态模式来封装事务的不同状态,并根据状态的变化执行相应的操作。这种设计使得事务管理的代码更加清晰、灵活和可扩展。如果需要添加新的状态或行为,只需要修改 State 枚举和相关方法,而不会影响整个事务管理框架的其他部分。

相关推荐
XiaoLeisj1 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck1 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
dayouziei1 小时前
java的类加载机制的学习
java·学习
Yaml43 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~3 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616883 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
aloha_7894 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
记录成长java4 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
睡觉谁叫~~~4 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust
程序媛小果5 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot