状态对于我们开发者而言是相当熟悉的,平常开发中总是离不开对象的状态,比如我们审核流程,就是一个状态不断流转的过程,订单流程也是一个状态不断流转的过程,而状态模式就是为了更好地解决我们状态流转过程中的业务处理逻辑。
状态模式(State Pattern)也称为状态机模式(State Machine Pattern),是允许对象在内部状态发生改变的同时改变对象的行为,使得看起来像是修改了类一样。
状态模式中的类的行为是由状态决定的,不同的状态有不同的行为。状态模式的意图是让一个对象在其内部改变时行为也随之一起改变。
状态模式的核心就是给每一种状态绑定一种行为。
好了,装逼时刻又到了:Talk is cheap,Show you the code,先看一个非常简单的例子。
下面我们就以订单状态的流转来写一个简单的示例,我们假设订单只有三个状态:待支付,待收货,已收货(完成)。
1、首先建立一个抽象的状态类,这个类需要定义所有状态的所有行为:
package com.zwx.design.pattern.state; public abstract class AbstractOrderState { protected OrderContext orderContext; public AbstractOrderState(OrderContext orderContext) { this.orderContext = orderContext; } public abstract void payOrder(); public abstract void deliver(); public abstract void receiveGoods(); }注意这里面集成了一个OrderContext对象,这个对象是用来负责状态的切换的。
2、因为有三个状态,所以我们新建三个具体的状态类来实现抽象状态类:
待支付状态类: package com.zwx.design.pattern.state; public class WaitPaidOrderState extends AbstractOrderState { public WaitPaidOrderState(OrderContext orderContext) { super(orderContext); } @Override public void payOrder() {//相当于待支付的状态绑定了支付行为 System.out.println("支付成功"); this.orderContext.setState(this.orderContext.waitDeliver);//切换状态 } @Override public void deliver() { System.out.println("对不起,请先付钱"); } @Override public void receiveGoods() { System.out.println("对不起,请先付钱"); } } 待发货状态类: package com.zwx.design.pattern.state; public class WaitDeliverOrderState extends AbstractOrderState { public WaitDeliverOrderState(OrderContext orderContext) { super(orderContext); } @Override public void payOrder() { System.out.println("你已经付过钱了"); } @Override public void deliver() { System.out.println("商品已发货并送达目的地"); this.orderContext.setState(this.orderContext.receiveGoods);//切换状态 } @Override public void receiveGoods() { System.out.println("请稍等,商品即将发货"); } } 已收货状态类: package com.zwx.design.pattern.state; public class ReceiveGoodsOrderState extends AbstractOrderState{ public ReceiveGoodsOrderState(OrderContext orderContext) { super(orderContext); } @Override public void payOrder() { System.out.println("您已经付过钱啦,不要重复付钱哦"); } @Override public void deliver() { System.out.println("商品已发货并送达,请不要重复发货"); } @Override public void receiveGoods() { System.out.println("用户已收到商品,此次交易结束"); } }我们可以看到,每个状态都绑定了一个行为(也可以支持绑定多个),并且在对应的行为处理完毕之后会流转到下一个状态,对于不属于当前状态的行为则做出相应的回应。
3、再建立一个状态上下文环境类,用来负责具体状态的切换:
package com.zwx.design.pattern.state; /** * @author zwx * @version 1.0 * @date 2020/10/5 * @since jdk1.8 */ public class OrderContext { AbstractOrderState waitPaid; AbstractOrderState waitDeliver; AbstractOrderState receiveGoods; AbstractOrderState currState;//当前状态 public OrderContext() { this.waitPaid = new WaitPaidOrderState(this); this.waitDeliver = new WaitDeliverOrderState(this); this.receiveGoods = new ReceiveGoodsOrderState(this); currState = waitPaid; } void setState(AbstractOrderState state){ this.currState = state; } public void payOrder(){ currState.payOrder(); } public void deliver(){ currState.deliver(); } public void receiveGoods(){ currState.receiveGoods(); } }可以看到,这个类里面也有点委派模式的影子,不同的行为委派给对应的对象进行处理而自己不做处理,只负责状态的切换。
4、最后再新建一个测试类进行测试:
package com.zwx.design.pattern.state; public class TestState { public static void main(String[] args) { OrderContext orderContext = new OrderContext(); orderContext.payOrder(); orderContext.deliver(); orderContext.receiveGoods(); } }输出结果为:
支付成功 商品已发货并送达目的地 用户已收到商品,此次交易结束从上面示例中,我们可以得出状态模式主要有3个角色:
环境类角色(Context):定义客户端需要的接口,内部维护一个当前状态实例,并负责具体状态的切换。抽象状态角色(State):定义每个状态下对应的行为,可以有一个或者多个行为。具体状态角色(ConcreteState):具体实现该状态对应的行为,并且在需要的情况下实现状态的切换。看这个状态模式的实现是有点和责任链模式相似,都是一条链去处理,可以这么说在某种场景下这两种模式可以互相替换,但是这两种模式也是有本质区别的。
状态模式: 状态模式的下一个节点是各个状态对象已经了解的,而且状态的流转就是由内部进行流转,客户端无法决定。责任链模式 “链路”上的对象并不知道下一个节点处理人是谁,而是由客户端自行组装决定的。状态模式和策略模式都能用来消除大量的if/else场景,但是也有本质区别。策略模式中各个策略之间是独立的,相互可以替换的,任意选择其中一个策略就能满足需求,而且是由客户端自己做出选择。而状态模式客户端只能选择初始节点,后续就会自动流转,各个状态是一个整体,不存在可以互相替换的状态。
当控制一个对象状态的条件表达式过于复杂的时候,就可以考虑使用状态模式,通过把状态的判断逻辑转移到表示不同状态的一系列类中,这样就可以把复杂的逻辑简单化,使得对象的行为依赖于它的状态,并且会随着状态的改变而同时改变行为。 状态模式主要应用于以下场景:
1、对象的行为需要随着状态的改变而改变时。2、当我们一个操作中需要根据状态来写大量的if/else逻辑时优点:
1、通过将每个状态设置为独立的对象,消除了代码中存在的大量if/else等判断分支,使得代码更加简洁,更容易维护。2、将不同的状态通过不同的类来表示,使得状态切换时相比较于用数字或者字符串来表示时更加直观,转换目的也更加明确。3、每个状态类的职责单一明确,易于扩展。缺点
1、状态过多会引起类膨胀(事实上这也是大部分设计模式的通病)。2、状态模式的结构与实现相对较为复杂,容易造成代码混乱。3、对于支持状态切换的状态类违反了开闭原则,因为一旦状态修改或者中间要新增状态,则需要修改对应的源代码,否则会出现状态切换错误。本文主要介绍了状态模式的定义,并通过一个简单的示例来实现了一个简单的状态模式,最后将状态模式和责任链模式进行了对比分析,明确了其和策略模式,责任链模式的区别。
请关注我,和孤狼一起学习进步。