前段时间一直在做个自助机项目,功能类似与火车站的自动售票机。不过我们做自助机是给酒店用的,替代前台的功能让旅客自己操作完成酒店入住这一过程,其中涉及到一个很核心的功能——自动发放房卡。也就是通过操作自助机自动出卡,而这一过程其中就包含一系列的步骤——发卡器准备、放卡(把卡移到卡槽)、读卡(读入房卡信息)、出卡、取卡。
当时看到这个功能时也是一头包,咋这么麻烦。怎么交差,怎么实现,怎么优雅的实现,总不会用万能的if/else嵌套吧,这代码不会爆炸。

冷静过后理智告诉我,先把功能梳理清晰,把问题抽离出来。不管怎么样的需求,多看多想,总能找到一个好方法实现。接下来我把功能梳理一遍,并画了一个简单流程图。

这不就是我们需要的实现的功能嘛,流程开始接着执行步骤一、步骤二、步骤三...直到完成,如果其中任意步骤失败就会结束整个过程。到这里我脑子里已经有了大概思路,怎么实现这个功能了。
停三分钟想想,是你的话你会怎么实现。。。
好了接下来就开始编码吧
首先实现一个接口,在这里面需要做几件事情。
- 执行当前任务
- 记录当前是步骤
public interface IStep {
/**
* 执行当前步骤
* @param caller 调用者上一步
* @param stepChain
* @param context
*/
void process(IStep caller,StepChain stepChain,StepContext context);
/**
* 记录当前步骤
* @param order
*/
void setOrder(int order);
/**
* 获取当前步骤
* @return
*/
int getOrder();
}
然后把每个步骤中需要的公共内容抽离出来,具体的实现只需要继承OrderStep即可
public abstract class OrderStep implements IStep{
private int order=0;
@Override
public void setOrder(int order) {
this.order=order;
}
@Override
public int getOrder() {
return this.order;
}
}
接下来就是最关键部分,如何控制这些步骤有序的运行下去
我们需要在StepChain中通过一个集合保存Step1、Step2、Step3...,然后并设置每个步骤当前的顺序。
public class StepChain implements IStep {
private int addIndex = 0;
private List<IStep> steps = new ArrayList<>();
private int index = 0;
public void addStep(IStep iStep) {
steps.add(iStep);
iStep.setOrder(addIndex++);
}
...
}
在process中处理执行逻辑:如果index等于steps.size说明已经运行到最后一步,则直接结束。否则让当前步骤继续执行下去。
具体实现如下:
public class StepChain implements IStep {
private int addIndex = 0;
private List<IStep> steps = new ArrayList<>();
private int index = 0;
public void addStep(IStep iStep) {
steps.add(iStep);
iStep.setOrder(addIndex++);
}
@Override
public void process(IStep caller, StepChain stepChain, StepContext context) {
if (index == steps.size()) return;
IStep iStep = steps.get(index);
if (caller != null && iStep.getOrder() <= caller.getOrder()) return;
index++;
iStep.process(caller, stepChain, context);
}
...
}
到这里主要的功能已经完成了,接下来测试一下我们的劳动成果吧。
那实现几个具体的步骤FirstStep、SecStep、ThirdStep、FinishStep
public class FirstStep extends OrderStep {
@Override
public void process(IStep caller, StepChain stepChain,StepContext context) {
System.out.println(System.currentTimeMillis()+" :执行第一步");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
stepChain.process(this,stepChain,context);//执行下一步
}
}
public class FinishStep extends OrderStep {
@Override
public void process(IStep caller, StepChain stepChain, StepContext context) {
System.out.println(System.currentTimeMillis()+"完成");
}
}
SecStep、ThirdStep的实现内容和FirstStep基本一样,我就不贴出来。
最后看一下如何使用StepChain来控制流程。
实现如下:
public class StepMain {
public static void main(String[] args) {
StepChain stepChain = new StepChain();
StepContext context = new StepContext();
stepChain.addStep(new FirstStep());
stepChain.addStep(new SecStep());
stepChain.addStep(new ThirdStep());
stepChain.addStep(new FinishStep());
stepChain.process(null,stepChain,context);
}
}
运行后的结果

现在已经可以按照顺序执行每个步骤,但还有一个小缺陷——如果其中某个步骤因为某种原因失败,这时候整个流程应该直接中断完成,这样才更符合具体的需求。这时候我们又要回到StepChain的process方法中。对当前的步骤进行判断,如果已中断则直接执行finish的步骤,否则正常继续下去执行。
具体实现如下:
public class StepContext {
private boolean isFinish=false;
public boolean isFinish() {
return isFinish;
}
public void setFinish(boolean finish) {
isFinish = finish;
}
}
修改StepChain的process方法
@Override
public void process(IStep caller, StepChain stepChain, StepContext context) {
if (context.isFinish()) {
index = steps.size()-1;
steps.get(index).process(caller,stepChain,context);
} else {
if (index == steps.size()) return;
IStep iStep = steps.get(index);
if (caller != null && iStep.getOrder() <= caller.getOrder()) return;
index++;
iStep.process(caller, stepChain, context);
}
}
这是时候只需在需要中断的步骤里要设置stepContex.setFinish(true)即可
public class FirstStep extends OrderStep {
@Override
public void process(IStep caller, StepChain stepChain,StepContext context) {
System.out.println(System.currentTimeMillis()+" :执行第一步");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
context.setFinish(true);
stepChain.process(this,stepChain,context);
}
}
我们在步骤一做了修改,按原先的假设如果成立,则控制台只会打印步骤一的日志和结束日志。
运行一下main方法看一下结果:

在很多开源的框架中也有类似的设计,比如OkHttp最核心也是最优雅的一部分——Interceptor,它把请求、缓存、桥接等各个功能都解耦成一个个的Interceptor,然后用一条责任链完美地串联在一起。
到这里StepChain这个小功能就完成了....需要源码的朋友戳这里https://github.com/wds1204/StepChain
风后面是风,天空上面是天空,而你的生活可以与众不同
网友评论