状态模式之可重用对象

什么是可重用状态对象

可重用状态对象是指一个状态对象可以被多个上下文(Context)实例共享,而不是为每个上下文单独创建新的状态对象。它的主要特点包括:

  • 状态对象是无状态的(stateless):状态对象本身不会存储特定上下文的数据,这样可以在多个上下文之间安全共享
  • 状态的行为被封装:状态对象只负责定义与其相关的行为,而上下文负责存储自身的数据
  • 通过共享减少资源消耗:避免为每个上下文重复创建相同的状态对象
    这种设计在需要频繁切换状态的场景中非常有效,例如工作流管理、任务状态管理、游戏开发等

为什么需要可重用状态对象

  • 减少内存开销:如果每个上下文都创建自己的状态对象,那么在大量上下文实例存在时,会导致内存占用过高。通过共享状态对象,可以显著减少内存使用
  • 提高性能:对象的创建和销毁是有成本的,特别是在状态频繁切换的情况下。共享状态对象可以避免重复的对象创建
  • 简化管理:状态对象的行为集中管理,易于维护和扩展

如何在Java中实现可重用状态对象

可以通过 状态模式(State Pattern) 和 单例模式(Singleton Pattern)结合来实现状态对象的共享

示例:订单状态共享

以下是一个简单的示例,展示如何在多个订单上下文中共享状态对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 定义状态接口
interface OrderState {
void handle(OrderContext context);
}

// 具体状态类:待支付
class PendingPaymentState implements OrderState {
// 使用单例模式实现状态对象共享
private static final PendingPaymentState instance = new PendingPaymentState();

private PendingPaymentState() {}

public static PendingPaymentState getInstance() {
return instance;
}

@Override
public void handle(OrderContext context) {
System.out.println("处理待支付状态。");
context.setState(PaidState.getInstance()); // 切换到已支付状态
}
}

// 具体状态类:已支付
class PaidState implements OrderState {
private static final PaidState instance = new PaidState();

private PaidState() {}

public static PaidState getInstance() {
return instance;
}

@Override
public void handle(OrderContext context) {
System.out.println("处理已支付状态。");
context.setState(ShippedState.getInstance()); // 切换到已发货状态
}
}

// 具体状态类:已发货
class ShippedState implements OrderState {
private static final ShippedState instance = new ShippedState();

private ShippedState() {}

public static ShippedState getInstance() {
return instance;
}

@Override
public void handle(OrderContext context) {
System.out.println("处理已发货状态。");
// 最终状态,无需切换
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 上下文类
class OrderContext {
private OrderState state;

public OrderContext(OrderState state) {
this.state = state;
}

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

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

// 测试
public class Main {
public static void main(String[] args) {
// 创建订单上下文,并初始化为待支付状态
OrderContext order1 = new OrderContext(PendingPaymentState.getInstance());
OrderContext order2 = new OrderContext(PendingPaymentState.getInstance());

// 处理订单1
order1.handle(); // 输出:处理待支付状态。
order1.handle(); // 输出:处理已支付状态。

// 处理订单2
order2.handle(); // 输出:处理待支付状态。
}
}

示例中的关键点解析

  • 状态对象的共享:
    每个具体状态类(如 PendingPaymentState、PaidState)通过单例模式实现
    这些状态对象是全局唯一的,可以在多个上下文实例中共享
  • 上下文存储自身数据:
    上下文类 OrderContext 持有当前状态的引用,并存储与该上下文相关的数据。状态对象本身不存储上下文数据,因此可以安全共享
  • 状态切换显式化:
    状态切换通过调用 context.setState() 方法完成,切换逻辑被显式地定义在状态对象中

优点

  • 内存效率高:状态对象是共享的,无需为每个上下文创建新的实例
  • 易于维护:状态的行为集中在具体状态类中,逻辑清晰
  • 可扩展性强:添加新状态时,只需增加新的状态类,不影响现有代码

适用场景

  • 工作流系统:例如订单处理、审批流程等
  • 游戏开发:例如角色的行为状态(行走、攻击、防御)
  • 任务管理系统:例如任务的状态(待处理、处理中、已完成)
  • 资源管理:例如共享连接池中的连接状态(空闲、使用中)

总结

通过将状态对象设计为可重用的单例对象,可以在 Java 中的各种上下文之间有效共享这些状态对象。这种设计不仅提高了内存和性能效率,还使状态管理更加清晰和易于维护。这种模式非常适合于需要频繁切换状态的场景,是状态模式的一种优化实现


状态模式之可重用对象
http://s1mplecode.com/design-pattern/reusable-state-objects.html
作者
Jago
发布于
2024年12月26日
许可协议