简介
定义:
当一个对象内在状态改变时允许改变其行为,这个对象看起来像是改变了其类(定义要看完整篇文章后再来看)
状态模式允许一个对象在内部状态改变时改变其行为。这意味着对象的行为取决于其当前状态,这使得状态模式在处理条件复杂的状态转换时非常有用。
使用场景:
你发现你的代码里面存在一个很长的`if else`列表,而这些分支都是因为不同状态下执行的操作不一样时考虑使用此模式。
UML类图:
状态模式共有3个角色
1、State是一个接口,封装了状态及其行为。
2、ConcreteState X:State的实现类,表示具体的状态。
3、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); // 若是null,抛出NullPointerException
logisticsState.doAction(this);
}
}
第三,实现各种状态类:
// 接单状态类,其需要实现`LogisticsState`接口
public class OrderState implements LogisticsState {
@Override
public void doAction(JdLogistics context) {
System.out.println("商家已经接单,正在处理中...");
}
}
// 出库状态类,其需要实现`LogisticsState`接口
public class ProductOutState implements LogisticsState {
@Override
public void doAction(JdLogistics context) {
System.out.println("商品已经出库...");
}
}
// 运输状态类,其需要实现`LogisticsState`接口
public class TransportState 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();
//运输状态
TransportState transportState = new TransportState();
jdLogistics.setLogisticsState(transportState);
jdLogistics.doAction();
}
}
结果输出:
商家已经接单,正在处理中...
商品已经出库...
商品正在运往苏州分发中心
可见,我们将每个状态下要做的具体动作封装到了每个状态类中,我们只需要切换不同的状态即可。如果不使用状态模式,我们的代码中可能会出现很长的`if else`列表,这样就不便于扩展和修改了。
总结
技术要点总结:
必须要有一个`Context`类,这个类持有`State`接口,负责保持并切换当前的状态。
状态模式没有定义在哪里进行状态转换,本例是在`Context`类进行的,也有在具体的`State`类中转换。
使用Context这么做的优缺点:
优点:
增强了程序的可扩展性,因为我们很容易添加一个State
增强了程序的封装性,每个状态的操作都被封装到了一个状态类中
缺点:
类变多了
看到这里,相信大家会有疑惑,这玩意和策略模式有点像啊?
状态模式VS策略模式:
状态模式与策略模式的UML类图都是一样的,从表面上看他们非常相似。特别是将状态切换任务放在Context中做的时候就更像了,但是其背后的思想却非常不同。
状态模式:想象你有一个电灯开关,电灯可以是开着的或关着的。状态模式让你能够根据不同的状态(比如电灯开着或关着)改变电灯的行为。当你按下开关时,如果电灯原本是关着的,它就会打开;如果电灯原本是开着的,它就会关闭。状态模式允许你定义一系列的状态,并且每个状态都有自己的行为。
策略模式:再想象你有一个计算器,它能够执行加法、减法、乘法和除法等不同的运算。策略模式让你能够定义一组算法(比如加法、减法等),然后在运行时选择使用哪一个算法。当你选择加法时,计算器就会执行加法运算;当你选择减法时,计算器就会执行减法运算。策略模式允许你定义一组可互换的算法,然后在运行时根据需要选择使用哪一个。
通俗区别:
状态模式:关注的是对象的“状态”,并且根据当前的状态来决定行为。就像电灯根据它是开着的还是关着的来决定是打开还是关闭。
策略模式:关注的是一组可以互换的“算法”或“策略”。就像计算器根据你选择的运算(加、减、乘、除)来决定执行哪种计算。
简而言之,状态模式是关于对象状态的,而策略模式是关于算法选择的。状态模式让你根据对象的当前状态来改变行为,策略模式让你在一组预定义的算法中选择一个来执行。