状态模式
- 一、介绍
- 二、订单处理系统中使用
- [三、Spring 中事务怎么使用状态模式?](#三、Spring 中事务怎么使用状态模式?)
一、介绍
状态模式(State Pattern)是一种行为型设计模式,它允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
状态模式的核心思想是将对象的状态封装成独立的类,使得对象可以根据自身的状态来获取不同的行为。当对象的内部状态发生改变时,其行为也会随之改变。
状态模式的主要角色有:
-
Context(上下文): 定义客户感兴趣的接口,并维护一个具体状态类的实例。
-
State(状态): 这是一个抽象状态类,它定义了一个与Context的一个或多个行为相关的接口。
-
ConcreteState(具体状态): 实现了抽象状态类所对应的行为。每一个子类实现对应于Context的一种状态。
状态模式的优点:
- 解耦了状态和行为,将它们分布到不同的类中,使得状态的改变不会影响行为的改变,行为的改变也不会影响状态的改变。
- 每种状态都可以根据自身的特点执行业务逻辑,因为每种状态对应一个子类。
- 支持对象状态的切换。
- 方便进行扩展,由于状态和行为是分开的,所以可以很方便地添加新的状态和行为。
状态模式的缺点:
- 会导致系统中类的个数增多。
- 在使用的过程中,需要分清系统中的每一种状态所对应的行为,以免产生错误。
状态模式的应用场景:
- 行为随状态改变而改变的场景。
- 一个操作中含有大量分支语句时,可以使用状态模式来把相关的分支语句转移到各个状态中。
- 处理订单流程、用户状态管理等领域都可以使用状态模式。
二、订单处理系统中使用
需求:一个简单的订单处理系统,订单从下单开始,经过多个状态的转换,最终到达完成状态。订单可能存在的状态包括:已下单(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(); // 订单已完成
}
}
OrderState
接口定义了订单可执行的操作,如支付、发货、签收、完成等。OrderedState
、PaidState
、ShippedState
、ReceivedState
、CompletedState
分别实现了订单在不同状态下的具体行为。Order
类是状态模式的上下文,它维护了订单的当前状态,并提供了相应的操作方法,如pay()
、ship()
等。这些方法内部会委托给当前状态对象执行具体的操作。Order
类的setState()
方法用于改变订单的当前状态。- 在客户端代码中,我们创建了一个订单对象,然后依次调用支付、发货、签收、完成操作,订单状态会随着操作的执行而发生改变。
通过状态模式的实现,将订单的不同状态和行为分离开来,使得订单在状态改变时可以自动切换行为,提高了代码的可扩展性和可维护性。同时,由于每个状态对应一个子类,我们也可以很方便地添加新的状态和行为,满足未来需求的变化。
三、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
类中的 invokeAfterCommit
和 invokeAfterRollback
方法就会根据事务的当前状态决定是否调用相应的同步回调方法
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
枚举和相关方法,而不会影响整个事务管理框架的其他部分。