设计模式-状态模式(State Pattern)
设计模式-状态模式(State Pattern)
概要
记忆关键词:状态变成类
定义:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
类型:行为型
状态模式结构图如下:
一、能解决什么问题
它主要用来解决对象在多种状态转换时,需要对外输出不同行为的问题。状态和行为是一一对应的,状态之间可以相互转换。当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。例如:淘宝下单,订单有待付款,已付款待发货,待收货,待评价, 已完成等状态。每个状态对应的行为都是不同的, 一个状态完成会流转到下一个状态。
通常对有状态的对象进行编程,我们的解决方案是:思考可能存在的所有状态,然后使用 if-else 或 switch-case 语句来进行状态判断,然后再根据不同的状态进行不同的处理。如上面的案例--淘宝下单:
public class ClientApplication { public static void main(String[] args) { String status = "待付款"; if ("待付款".equals(status)) { // 执行付款逻辑 } else if("待发货".equals(status)) { // 执行发货逻辑 } else if("待收货".equals(status)) { // 执行收货逻辑 } else if("待评价".equals(status)) { // 执行评价逻辑 } } }
大量的if...else的缺点很明显:
- 违背开闭原则: 当增加一种状态的时候, 需要修改原来的逻辑。
- 当状态很多的时候, 代码段很长, 臃肿, 不容易维护, 可扩展性差。
二、涉及的角色
1. State 抽象状态类
定义一个接口以封装与Context的一个特定状态相关的行为
代码示例如下:
1 public interface State { 2 3 /** 4 * 处理状态 5 * 6 * @param context 状态 7 */ 8 void handleState(Context context); 9 }
2. ConcreteState(具体状态实现类)
具体状态实现类,每一个子类实现一个与Context的一个状态相关的行为
代码示例如下:
具体实现类一:
1 public class ConcreteState1 implements State{ 2 @Override 3 public void handleState(Context context) { 4 System.out.println("Handling state 1"); 5 // 在这里执行与状态1相关的行为 6 // 可能会改变上下文的状态 7 context.setState(new ConcreteState2()); 8 } 9 }
具体实现类二:
1 public class ConcreteState2 implements State{ 2 @Override 3 public void handleState(Context context) { 4 System.out.println("Handling state 2"); 5 // 在这里执行与状态2相关的行为 6 // 可能会改变上下文的状态 7 context.setState(new ConcreteState1()); 8 } 9 }
3. Context(上下文环境类)
维护一个ConcreteState子类的实例,这个实例定义当前的状态
代码示例如下:
1 public class Context { 2 3 /** 4 * 在环境类中维护一个抽象状态State的实例, 这个实例存储当前状态。 5 */ 6 private State state; 7 8 public Context() { 9 this.state = new ConcreteState1(); 10 } 11 12 public void setState(State state) { 13 this.state = state; 14 } 15 16 public State getState() { 17 return state; 18 } 19 20 /** 21 * 在环境类中定义所有状态执行的方法. 22 */ 23 public void request() { 24 // 委托给具体状态类处理 25 state.handleState(this); 26 } 27 }
客户端调用:
1 public class Client { 2 public static void main(String[] args) { 3 Context context = new Context(); 4 context.request(); 5 System.out.println("当前状态为:" + context.getState()); 6 7 // 连续请求,观察状态的变化 8 context.request(); 9 System.out.println("当前状态为:" + context.getState()); 10 11 // 连续请求,观察状态的变化 12 context.request(); 13 System.out.println("当前状态为:" + context.getState()); 14 } 15 } 16 17 18 //运行结果: 19 Handling state 1 20 当前状态为:org.example.state.ConcreteState2@7d4991ad 21 Handling state 2 22 当前状态为:org.example.state.ConcreteState1@28d93b30 23 Handling state 1 24 当前状态为:org.example.state.ConcreteState2@1b6d3586
三、算法分析
1. 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况
把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。当然,如果这个状态判断很简单,那就没必要用状态模式了。
2. 帮助对象通过改变它们内部的状态来控制其行为
3. 当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。
四、状态模式与策略模式的区别
1. 状态模式和策略模式的 UML 一样,但是解决的问题和侧重不一样
2. 状态模式重点在各状态之间的切换从而做不同的事情,而策略模式更侧重于根据具体情况选择策略,并不涉及切换
3. 状态模式不同状态下做的事情不同,而策略模式做的都是同一件事
4. 状态模式封装了对象的状态,而策略模式封装算法或策略。因为状态是跟对象密切相关的,它不能被重用;而通过从Context中分离出策略或算法,我们可以重用它们
5. 在状态模式中,每个状态通过持有Context的引用,来实现状态转移;但是每个策略都不持有Context的引用,它们只是被Context使用。
四、使用场景
在订单系统中,通过使用状态模式,可以将订单的每个状态和对应的行为封装起来,增强代码的灵活性和可扩展性。
实现流程简单说明:
1)OrderContext:作为上下文,维护订单的当前状态,允许通过状态切换来改变订单的行为。
2)状态类(如 PendingPaymentState, PaidState, ShippedState, CompletedState, CanceledState):每个状态类负责订单在该状态下的具体行为,并且决定订单如何从当前状态切换到下一个状态。
3)状态切换:在订单流程的每一步操作中(如支付、发货、收货),通过 context.setState(newState) 来进行状态转换。
参考链接:
https://www.cnblogs.com/ITPower/p/14980134.html