一:简介
状态模式设计模式是一种行为型的设计模式。当一个对象内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。定义对于初学者没啥用,因为字都认识却无法理解其中的含义。必须等学完了,回过头来看才能更加深刻的理解其含义。
使用场景
你发现你的代码里面存在一个很长的if else列表,而这些分支都是因为不同状态下执行的操作不一样时考虑使用此模式
UML类图
照例先上一张俺手撕的UML类图
从上图可见,状态模式共有3个角色
State:是一个接口,封装了状态及其行为
ConcreteState X:State的实现类,表示具体的状态
Context:保持并切换各个状态,其持有一个State的引用。它将依赖状态的各种操作委托给不同的状态对象执行。其负责与客户端交互
实例
这个物流系统就很适合使用状态模式来开发,因为此过程存在很多不同的状态,例如接单,出库,运输,送货,收货,评价等等。而订单在每个不同的状态下的操作可能都不一样,例如在接单状态下,商家就需要通知仓库拣货,通知用户等等操作,其他状态类似。
第一,定义一个状态接口
此接口定义各个状态的统一操作接口
public interface LogisticsState { void doAction(JdLogistics context); }
第二,定义一个物流Context类
此类持有一个LogisticsState 的引用,负责在流程中保持并切换状态,这个类是一个重点,引用持有一个接口类。
public class JdLogistics { private LogisticsState logisticsState; public void setLogisticsState(LogisticsState logisticsState) { this.logisticsState = logisticsState; } public LogisticsState getLogisticsState() { return logisticsState; } public void doAction(){ Objects.requireNonNull(logisticsState); logisticsState.doAction(this); } }
第三,实现各种状态类
接单状态类,其需要实现LogisticsState接口
public class OrderState implements LogisticsState { @Override public void doAction(JdLogistics context) { System.out.println("商家已经接单,正在处理中..."); } }
出库状态类
public class ProductOutState implements LogisticsState { @Override public void doAction(JdLogistics context) { System.out.println("商品已经出库..."); } }
第四, 客户端使用
public class StateClient { public void buyKeyboard() { //状态的保持与切换者 JdLogistics jdLogistics = new JdLogistics(); //接单状态 OrderState orderState = new OrderState(); jdLogistics.setLogisticsState(orderState); jdLogistics.doAction(); //出库状态 ProductOutState productOutState = new ProductOutState(); jdLogistics.setLogisticsState(productOutState); jdLogistics.doAction(); } public static void main(String[] args) { new StateClient().buyKeyboard(); } }
输出结果:
商家已经接单,正在处理中...
商品已经出库...
可见,我们将每个状态下要做的具体动作封装到了每个状态类中,我们只需要切换不同的状态即可。如果不使用状态模式,我们的代码中可能会出现很长的if else列表,这样就不便于扩展和修改了。
技术要点总结
必须要有一个Context类,这个类持有State接口,负责保持并切换当前的状态。状态模式没有定义在哪里进行状态转换,本例是在Context类进行的,也有人在具体的State类中转换。
当使用Context类切换状态时,状态类之间互相不认识,他们直接的依赖关系应该由客户端负责。 例如,只有在接单状态的操作完成后才应该切换到出库状态,那么出库状态就对接单状态有了依赖,这个依赖顺序应该由客户端负责,而不是在状态内判断。
当使用具体的State类切换时,状态直接就可能互相认识,一个状态执行完就自动切换到了另一个状态去了
优缺点
优点:增强了程序的可扩展性,因为我们很容易添加一个State;增强了程序的封装性,每个状态的操作都被封装到了一个状态类中
缺点:类变多了
状态模式与策略模式
状态模式与策略模式的UML类图都是一样的,从表面上看他们非常相似。特别是将状态切换任务放在Context中做的时候就更像了,但是其背后的思想却非常不同。
策略模式定义了一组可互相代替的算法,这一组算法对象完成的是同一个任务,只是使用的方式不同,例如同样是亿万富翁,马云通过卖东西实现,而王思聪通过继承实现。
状态模式不同的状态完成的任务完全不一样。