一. 什么是状态模式?
状态模式(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)) { // 执行评价逻辑 } else if("待收货".equals(status)) { // 执行收获逻辑 } } }
大量的if...else的缺点很明显
- 违背开闭原则: 当增加一种状态的时候, 需要修改原来的逻辑
- 当状态很多的时候, 代码段很长, 臃肿, 不容易维护, 可扩展性差.
状态模式可以很好地解决这个问题。
状态模式的思想:当是条件语句表示一个对象状态转换过于复杂时,可以把条件判断中的“判断逻辑”提取出来,放在单独的类中,当前上下文处于那种状态,直接用相应的
状态类对象进行处理,这样的好处是:能把原来复杂的判断逻辑简单化,消除了 if-else、switch-case 等条件判断语句,代码更有层次性,且具备良好的扩展力, 可维护性。
二. 状态模式的结构
状态模式把因环境改变而变化的对象行为包装在不同的状态类中,其目的保证对象的状态变化时, 其行为也随之改变。接下来, 我们来看看状态模式的结构。
从中我们可以看出有四个组成部分:
1. 环境类
环境类也是上下文类, 状态的变化依赖于环境, 环境记录当前的状态. 环境类拥有多种状态的对象. 环境类的状态存在多样性, 不同状态下对象的行为有所不同, 因此将状态独立出去形成单独的状态类。
> 1) 在环境类中维护一个抽象状态State的实例, 这个实例存储当前状态。 > 2) 在环境类中定义所有状态执行的方法.
package com.lxl.www.designPatterns.statePattern.context; /** * 上下文环境类 */ public class Context { // 在环境类中维护一个抽象状态State的实例, 这个实例存储当前状态。 private State state; public Context() { this.state = new ConcreteStateA(this); } public void setState(State state) { this.state = state; } /** * 在环境类中定义所有状态执行的方法. */ public void handler1() { this.state.handler1(); } public void handler2() { this.state.handler2(); } }
2. State抽象状态类
抽象环境中声明一个环境角色,提供各个状态类自行访问,并且提供所有状态的抽象行为,由各个实现类实现。
/** * 状态抽象类 */ public abstract class State { /** * 环境上下文 */ public Context context; public State(Context context) { this.context = context; } /** * 定义了所有状态的抽象方法 */ abstract void handler1(); abstract void handler2(); }
3. 具体状态
具体状态实现,这里以定义ConcreteStateA、ConcreteStateB、ConcreteStateC三个具体状态类。具体状态类继承自State抽象类, 并实现抽象方法。
package com.lxl.www.designPatterns.statePattern.context; public class ConcreteStateA extends State{ public ConcreteStateA(Context context) { super(context); } @Override void handler1() { System.out.println("执行ConcreteStateA中handler1的逻辑"); this.context.setState(new ConcreteStateB(context)); } @Override void handler2() { System.out.println("执行ConcreteStateA中handler2的逻辑"); this.context.setState(new ConcreteStateC(context)); } }
在这里, 我们重写了状态类的状态方法。当执行完方法一的业务逻辑后, 将状态变更为 ConcreteStateB. 执行完handler2的业务逻辑后, 将状态变更为ConcreteStateC。
这种自动变更状态, 在调用方是无感知的。
4. Client客户端
构建Context环境上下文类的实例对象,初始化时设置初始状态是ConcreteStateA,执行行为观察结果。
package com.lxl.www.designPatterns.statePattern.context; public class Client { public static void main(String[] args) { Context context = new Context(); context.handler1(); context.handler1(); context.handler1(); context.handler2(); } }
全部源码:
package com.lxl.www.designPatterns.statePattern.context; /** * 上下文环境类 */ public class Context { // 在环境类中维护一个抽象状态State的实例, 这个实例存储当前状态。 private State state; public Context() { this.state = new ConcreteStateA(this); } public void setState(State state) { this.state = state; } /** * 在环境类中定义所有状态执行的方法. */ public void handler1() { this.state.handler1(); } public void handler2() { this.state.handler2(); } } /** * 状态抽象类 */ public abstract class State { /** * 环境上下文 */ public Context context; public State(Context context) { this.context = context; } /** * 定义了所有状态的抽象方法 */ abstract void handler1(); abstract void handler2(); } public class ConcreteStateA extends State{ public ConcreteStateA(Context context) { super(context); } @Override void handler1() { System.out.println("执行ConcreteStateA中handler1的逻辑"); this.context.setState(new ConcreteStateB(context)); } @Override void handler2() { System.out.println("执行ConcreteStateA中handler2的逻辑"); this.context.setState(new ConcreteStateC(context)); } } public class ConcreteStateB extends State{ public ConcreteStateB(Context context) { super(context); } @Override void handler1() { System.out.println("执行ConcreteStateB中handler1的逻辑"); this.context.setState(new ConcreteStateC(context)); } @Override void handler2() { System.out.println("执行ConcreteStateB中handler2的逻辑"); this.context.setState(new ConcreteStateA(context)); } } public class ConcreteStateC extends State{ public ConcreteStateC(Context context) { super(context); } @Override void handler1() { System.out.println("执行ConcreteStateC中handler1的逻辑"); this.context.setState(new ConcreteStateA(context)); } @Override void handler2() { System.out.println("执行ConcreteStateC中handler2的逻辑"); this.context.setState(new ConcreteStateB(context)); } } public class Client { public static void main(String[] args) { Context context = new Context(); context.handler1(); context.handler1(); context.handler1(); context.handler1(); } }
客户端直接结果分析:
- Context实例花的时候, 状态是 ConcreteStateA
- 第一次执行context.handler1();将状态切换到了ConcreteStateB
- 第二次执行context.handler1();将状态切换到了ConcreteStateC
- 第三次执行context.handler1();将状态切换到了ConcreteStateA
所以, 最后的运行结果是:
执行ConcreteStateA中handler1的逻辑
执行ConcreteStateB中handler1的逻辑
执行ConcreteStateC中handler1的逻辑
执行ConcreteStateA中handler1的逻辑
三. 状态模式的应用实例
下面以商品下单为例,来看看状态模式的应用。
在电商系统购物的过程中,订单一般有这几种状态,待付款,待出库,待收货, 待评价,完成。不同的状态下,用户看到的行为是不同的。
我们由浅入深, 来看看几种状态模式的实现。
第一种: 状态是一条线的实现逻辑
先来看看这种方法的流程:
这种状态的特点是: 上一个状态流转到下一个状态, 下一个流转到下下个,以此类推。直到最后一个没有状态流转了。来看看源码:
第一步:定义上下文环境类。
用orderStatus来记录当前的状态。并定义了所有状态的方法。
package com.lxl.www.designPatterns.statePattern.order; /** * 订单上下文 */ public class OrderContext { /**在上下文类中记录订单状态*/ private IOrderStatus orderStatus; public OrderContext() { // 最开始是待付款状态 this.orderStatus = new Pending(); } public void setOrderStatus(IOrderStatus orderStatus) { this.orderStatus = orderStatus; } /** * 业务逻辑操作 */ public void businessHandler() { this.orderStatus.businessHandler(this); } /** * 打印当前业务 */ public void printInfo() { this.orderStatus.printInfo(); } }
第二步: 定义状态抽象类。
里面定义所有状态类要实现的方法。
package com.lxl.www.designPatterns.statePattern.order; /** * 订单状态 */ public interface IOrderStatus { void businessHandler(OrderContext context); void printInfo(); }
这里面就有两个方法, 一个是状态要执行的业务逻辑, 另一个是打印当前状态。
第三步:定义状态具体类。
这里定义了5个具体类,分别是:待付款,待出库,待收货, 待评价,完成
package com.lxl.www.designPatterns.statePattern.order; /** * 待付款 */ public class Pending implements IOrderStatus{ @Override public void businessHandler(OrderContext context) { //执行业务, 完成付款, 进入到下一个状态 System.out.println("付款完成"); context.setOrderStatus(new WaitOut()); } @Override public void printInfo() { System.out.println("当前状态等::待付款"); } } package com.lxl.www.designPatterns.statePattern.order; /** * 待出库 */ public class WaitOut implements IOrderStatus{ @Override public void businessHandler(OrderContext context) { //执行业务, 完成付款, 进入到下一个状态 System.out.println("货物已出库"); context.setOrderStatus(new WaitReceive()); } @Override public void printInfo() { System.out.println("当前状态等::待出库"); } } package com.lxl.www.designPatterns.statePattern.order; /** * 待收货 */ public class WaitReceive implements IOrderStatus { @Override public void businessHandler(OrderContext context) { //执行业务, 完成付款, 进入到下一个状态 System.out.println("已经收货"); context.setOrderStatus(new OrderEvaluation()); } @Override public void printInfo() { System.out.println("当前状态等::待收货"); } } package com.lxl.www.designPatterns.statePattern.order; /** * 订单评价 */ public class OrderEvaluation implements IOrderStatus{ @Override public void businessHandler(OrderContext context) { //执行业务, 完成付款, 进入到下一个状态 System.out.println("已经评价"); context.setOrderStatus(new Finish()); } @Override public void printInfo() { System.out.println("当前状态等::待评价"); } } package com.lxl.www.designPatterns.statePattern.order; /** * 订单完成 */ public class Finish implements IOrderStatus{ @Override public void businessHandler(OrderContext context) { // 执行业务, 完成付款, 进入到下一个状态 System.out.println("完成工作处理完毕"); } @Override public void printInfo() { System.out.println("当前状态::订单完成"); } }
第四步: 定义客户端类, 模拟下单的流程
package com.lxl.www.designPatterns.statePattern.order; public class OrderClient { public static void main(String[] args) { OrderContext orderContext = new OrderContext(); // 开始下单 System.out.println("==========开始下单=========="); orderContext.printInfo(); // 付款 System.out.println("==========付款=========="); orderContext.businessHandler(); orderContext.printInfo(); // 货物出库 System.out.println("==========货物出库=========="); orderContext.businessHandler(); orderContext.printInfo(); // 收货 System.out.println("==========收货=========="); orderContext.businessHandler(); orderContext.printInfo(); // 评价订单 System.out.println("==========评价订单=========="); orderContext.businessHandler(); orderContext.printInfo(); // 订单完成 System.out.println("==========订单完成=========="); orderContext.businessHandler(); } }
接下来我们来看看运行效果:
开始下单
当前状态等::待付款
付款
付款完成
当前状态等::待出库
货物出库
货物已出库
当前状态等::待收货
收货
已经收货
当前状态等::待评价
评价订单
已经评价
当前状态::订单完成
订单完成
完成工作处理完毕