允许一个对象在其内部状态改变时改变它的行为
说人话:允许对象在改变自身状态时候,更改绑定的特定方法
【产品】:Hello,开发小哥,我们需要开发一款 娃娃机
,你可以提前想想怎么设计它啦。
【开发】:娃娃机?我想想奥,它需要投币,用户移动,确认抓取,结束这几个动作,好像很好做欸,用一个变量维护它当前的阶段,然后写四个 if 语句就好啦。
【BOSS】:你准备用一个主方法,四个子方法配合 if 语句外加一个状态变量去做吗?
// 伪代码 public void handle() { if (flag == A) { a(); } if (flag == B) { b(); } } 复制代码
【开发】:对啊,老大,你真是我肚子里的蛔虫!
【BOSS】:蛔你个头,这样做 大错特错! ,你难道想对 投币口,按钮,摇杆都绑定同一个方法吗?
【开发】:对哦,它们应该是 不同的方法,同时暴露给用户,我再思考思考
「定义状态接口,同时封装变化,利用default关键字封装默认方法」
public interface State { /** 投币 **/ default void giveMoney() { System.out.println("无法投币"); } /** 移动滑杆 **/ default void move() { System.out.println("无法移动滑杆"); } /** 抓取 **/ default void grab() { System.out.println("无法抓取"); } void changeState(); } 复制代码
「投币状态 状态的其中之一」
public class MoneyState implements State{ Context context; public MoneyState(Context context) { this.context = context; } @Override public void giveMoney() { System.out.println("已投币!"); changeState(); } @Override public void changeState() { context.setExecute(new MoveState(context)); } } 复制代码
为了尽量减少代码,只展示了其中一种状态,我们可以看到在 MoneyState 状态类执行所属的业务方法时,更改了上下文持有的状态类,这就产生了 状态的变更 ,同时上下文更加清晰,即:我只用考虑我下一个状态是什么
状态模式的设计思路:
简单来说,
❝如果看着有点模棱两可,建议看完本文后,访问专题设计模式开源项目,里面有具体的代码示例,链接在最下面
❞
❝就好像娃娃机运作的多种状态, 投币,移动摇杆,按下确认按钮等等可能不按先后顺序触发
❞
上文中我们大概知道了状态模式的特点,把状态封装成类,在调用状态-核心方法时候更改其状态本身,此时考虑的多种状态方法名可能各不相同,假设我们都起一样的名字会如何?
我们会首先遇到一个问题,我们无法得知它需要调用几次方法(因为可能有重复性 A - B 的情况),但如果无限循环,在适当的地方控制其结束点,和是否继续执行的标识,好像就可以解决了。
简单描述下即:开始处理订单
「Context 上下文」
public class Context { /** * 最大执行次数 */ public static final Integer FAIL_NUM = 3; /*** * 失败次数 */ private int failNum; /** * 是否继续执行的标识 */ private boolean isAbandon; /*** * 当前状态 */ private StateInterface stateInterface; public Context() { this.stateInterface = new HandleOrder(); this.failNum = 1; this.isAbandon = false; } /*** * 处理方法 */ public void handle () { stateInterface.doAction(this); } // 省略无用代码... } 复制代码
「处理订单状态」
public class HandleOrder implements StateInterface { @Override public void doAction(Context context) { printCurrentState(); // do somethings int num = (int) (Math.random() * 11); if (num >= 8) { System.out.println("处理订单完成, 进入成功状态..."); context.setStateInterface(new SuccessOrder()); } else { System.out.println("处理订单失败, 进入失败状态..."); context.setStateInterface(new FailOrder()); } CodeUtils.spilt(); } @Override public StateEnums getCurrentState() { return StateEnums.HANDLE_ORDER; } } 复制代码
「客户端调用方法」
public class App { public static void main(String[] args) { // 模拟从队列中取任务按流程循环执行 Context context = new Context(); while (true) { // 校验是否为废弃 | 已完成任务 if (context.isAbandon()) { System.out.println("此条任务不再执行... "); break; } context.handle(); } } } 复制代码
测试结果输出:
# 当前状态:订单处理 # 处理订单失败, 进入失败状态... # ------------------------ # 当前状态:处理订单失败 # 订单处理失败... 当前执行次数: 1 # ------------------------ # 当前状态:订单处理 # 处理订单失败, 进入失败状态... # ------------------------ # 当前状态:处理订单失败 # 订单处理失败... 当前执行次数: 2 # ------------------------ # 当前状态:订单处理 # 处理订单完成, 进入成功状态... # ------------------------ # 当前状态:处理订单成功 # 订单处理完成 -> 进入入库逻辑... # 入库处理完成 # ------------------------ # 此条任务不再执行... 复制代码
❝如果看着有点模棱两可,建议看完本文后,访问专题设计模式开源项目,里面有具体的代码示例,链接在最下面
❞
在这样的设计中,与其说是状态的变更,不如说是 “流程” 的变更更为贴切,因此它可以作为诸多后台任务的解决方案,尤其是面临很多业务流程场景时,可以极大的提高代码的可维护性: 我只用考虑和我有关的 “流程”
「附上GOF一书中对于状态模式的UML图:」
GitHub地址