一、为什么用状态模式?
在开发过程中,你是否遇到过这样的难题:对象需要根据不同的状态执行不同行为,但代码中却充斥着大量的if-else或switch-case语句?
随着状态的增多,代码变得臃肿且难以阅读,不仅增加了维护成本,还容易在新增或修改状态逻辑时引入Bug。更糟糕的是,条件判断会导致代码高度耦合,每次状态变化都需要修改多个地方,牵一发而动全身。
状态模式(State Pattern)正是为了解决这些问题而生。它通过将每种状态的行为封装为独立的类,并由上下文类动态管理状态切换,不仅消除了冗长的条件判断,还使状态切换逻辑清晰可控,行为易于扩展。
二、什么是状态模式?
状态模式(State Pattern)是一种行为型设计模式,通过将对象在不同状态下的行为封装到独立的状态类中,动态改变对象的行为,从而达到清晰解耦的目的。
其核心思想是:将“状态”和“行为”分离,让对象的行为随状态变化而变化,同时简化复杂的判断逻辑。
状态模式的核心组件:
1. 上下文类(Context)
维护当前的状态实例,负责具体状态的切换,对外提供统一的操作接口。
2. 抽象状态类(State)
定义所有具体状态的公共行为接口,通常是一个接口或抽象类,明确在该状态下可执行的行为。
3. 具体状态类(Concrete State)
实现抽象状态接口,封装对应状态的具体行为逻辑,并在需要时进行状态切换。
在状态模式中,客户端无需关心状态切换逻辑,所有状态管理均由状态模式内部完成,让代码更清晰、易扩展。
三、状态模式代码示例
示例场景:订单系统的状态包括“待付款”、“已付款”、“已发货”和“已完成”。使用状态模式将这些状态的行为独立封装为不同的类,具体代码如下。
1. 订单状态抽象类-接口
// 状态接口:定义所有具体状态的共同行为
public interface OrderState {// 处理订单的方法,接收上下文类作为参数void handle(Order context);
}
2. 具体状态类 - 待付款
// 具体状态 - 待付款
public class PendingPaymentState implements OrderState {@Overridepublic void handle(Order context) {System.out.println("订单待付款,请尽快付款。");// 订单付款后切换到已付款状态context.setState(new PaidState()); // 状态切换由状态类决定}
}
3.具体状态类 - 已付款
// 具体状态 - 已付款
public class PaidState implements OrderState {@Overridepublic void handle(Order context) {System.out.println("订单已付款,准备发货。");// 切换到“已发货”状态context.setState(new ShippedState());}
}
4.具体状态类 - 已发货
// 具体状态 - 已发货
public class ShippedState implements OrderState {@Overridepublic void handle(Order context) {System.out.println("订单已发货,等待客户收货。");// 订单已发货,切换到终态context.setState(new FinalState()); // 如果不再有其他状态,切换到终态}
}
5.具体状态类 - 终态类
// 终态类 - 表示订单处理结束的状态
public class FinalState implements OrderState {@Overridepublic void handle(Order context) {System.out.println("订单已完成,无需进一步操作。");}
}
6. 上下文类 - 订单类
// 上下文类 - 订单类,维护当前状态对象
public class Order {// 当前订单的状态private OrderState state;// 构造函数,初始化时默认订单为待付款状态public Order() {this.state = new PendingPaymentState(); }// 设置当前状态(供状态类调用)protected void setState(OrderState state) {this.state = state;}public void handleOrder() {state.handle(this); // 委托当前状态处理}
}
7. 测试类
public class Main {public static void main(String[] args) {// 创建订单对象,初始状态为待付款Order order = new Order();// 订单处于待付款状态,执行相应操作order.handleOrder(); // 输出:订单待付款,请尽快付款。// 订单状态已改变为已付款order.handleOrder(); // 输出:订单已付款,准备发货。// 订单状态已改变为已发货order.handleOrder(); // 输出:订单已发货,等待客户收货。// 订单状态已改变为最终状态order.handleOrder(); // 输出:订单已完成,无需进一步操作。}
}
运行结果
订单待付款,请尽快付款。
订单已付款,准备发货。
订单已发货,等待客户收货。
订单已完成,无需进一步操作。
代码说明:
1.OrderState订单状态接口
• 定义所有状态类的公共行为接口,提供抽象方法handle()。
• handle(Order context) 接收上下文对象,处理当前状态的逻辑,并根据需要切换到下一个状态。
2.具体状态类
具体状态类实现OrderState接口,并在handle()方法中定义各自的具体行为:
• PendingPaymentState
订单的“待付款”状态。付款完成后,调用上下文对象的setState()方法,将状态切换到PaidState已付款状态。
• PaidState
订单的“已付款”状态。调用上下文对象的setState()方法,将状态切换到ShippedState已发货状态。
• ShippedState
订单的“已发货”状态。调用上下文对象的setState()方法,将状态切换到FinalState终态。
• FinalState
订单的“已完成”状态。作为终态,不再进行任何状态切换。
3. Order 类(上下文类)
维护订单的当前状态,并委托状态类执行具体的行为,同时提供状态切换机制。
• setState(OrderState state):供状态类调用,用于切换订单状态。
• handleOrder():委托当前状态对象执行相应的逻辑。
4.Main(测试类)
创建Order对象,模拟订单状态的变化过程。每次调用handleOrder(),观察状态切换和对应的行为输出。
四、状态模式的优势与不足
优势:
1.降低耦合
客户端只需与上下文类交互,无需直接操作具体状态类,降低了客户端与实现的耦合。
2.清晰的状态行为封装
每种状态的行为被独立封装在对应的状态类中,避免了上下文类中冗长的条件判断,代码更清晰、易读、易维护。
3.灵活性高
状态切换逻辑被封装在状态类中,使得状态转换更加灵活且可控。
不足:
1.类数量增加:每种状态都需要定义一个独立的类,状态较多时会导致类数量大幅增加,提升系统复杂度。
2.有限的开闭性:当状态之间存在复杂的关联逻辑时,新增状态可能会影响已有状态类,并未完全符合开闭原则。
3.不适合简单场景:对于状态较少或逻辑简单的场景,直接使用条件语句可能更高效,状态模式反而会显得繁琐。
五、状态模式的适用场景
1.对象行为依赖状态变化:如订单状态、权限管理、工作流控制等场景。
2.复杂状态管理:状态之间有严格的转换规则,需对状态切换过程进行控制的场景。
3.替代条件判断:希望用更优雅的方式替代if-else或switch-case来处理多状态逻辑的场景。
六、总结
状态模式将不同状态的行为封装到独立类中,使得状态切换和管理变得清晰。它消除了大量条件判断的复杂性,还显著提升了代码的扩展性和维护性。
在多状态管理和频繁状态切换的场景中,状态模式提供了一种优雅、高效的解决方案。