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 枚举和相关方法,而不会影响整个事务管理框架的其他部分。

相关推荐
奋进的芋圆21 小时前
Java 延时任务实现方案详解(适用于 Spring Boot 3)
java·spring boot·redis·rabbitmq
sxlishaobin21 小时前
设计模式之桥接模式
java·设计模式·桥接模式
model200521 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
荒诞硬汉21 小时前
JavaBean相关补充
java·开发语言
提笔忘字的帝国21 小时前
【教程】macOS 如何完全卸载 Java 开发环境
java·开发语言·macos
2501_941882481 天前
从灰度发布到流量切分的互联网工程语法控制与多语言实现实践思路随笔分享
java·开发语言
華勳全栈1 天前
两天开发完成智能体平台
java·spring·go
alonewolf_991 天前
Spring MVC重点功能底层源码深度解析
java·spring·mvc
沛沛老爹1 天前
Java泛型擦除:原理、实践与应对策略
java·开发语言·人工智能·企业开发·发展趋势·技术原理
专注_每天进步一点点1 天前
【java开发】写接口文档的札记
java·开发语言