摘自:https://www.jianshu.com/p/8def04b34b3c

首先,了解状态机是什么,我们为什么需要状态机!
举个最简单例子,请假,作为一个最底层程序员,每次请假都要领导层层审批,而假有分为很多种,事假,病假,婚假,年休假等等,当然选择请的假不同,审批标准也不同,不同的假单需要走的审批链也不一样,比如年休假,可能只需要领导审批扣掉年休假即可,请病假需要领导审批,领导审批之后,先休假,等休完假回来提交病假的材料,由hr审批之后才能完成整个请假过程。更有甚者,如果你要修一个一个月的长假,就不仅仅是需要直线领导hr审批,可能还需要公司ceo审批 ,审批通过后,才能通过。如下图:

当然,实际来讲,请假的种类和链路比这个要复杂的多,我们一般会怎么实现,是否要使用if else了,对应不同的假单,走不同的分支,代码写出来就成了一个非常复杂的,多级嵌套的代码了,后面如何维护代码,多了几种假的种类,是不是又要if else了。如下代码:
 public void requestLeavePermit(String type){
if(type.equals("事假")){
//领导审批->hr审批->ceo审批->完成
}else if(type.equals("病假")){
//领导审批->休假->补充病例->hr审批->完成
}else if(type.equals("年休假")){
//领导审批->hr审批->通过
}else if(type.equals("产假")){
//领导审批->hr审批->通过
}else if(type.equals("调休假")){
//领导审批->ceo审批->通过
}
}

  或者写成这个样子:

public void requestLeavePermit(String type,String userName){
switch (type){
case "事假":
//领导审批->hr审批->ceo审批->完成
break;
case "病假":
//领导审批->休假->补充病例->hr审批->完成
break;
case "年休假":
//领导审批->hr审批->通过
break;
case "产假":
//领导审批->hr审批->通过
break;
case "调休假":
//领导审批->ceo审批->通过
default:
break;
}
}

  

if,else嵌套太深,然后每个if,else又是自己的处理流程,这样代码结构会原来越复杂,当审批链发生变更,这个时候会发现代码耦合性太强,导致修改起来很麻烦。
如何解决这个问题,我们不难看到,所有的请假都经过了这样几个阶段,从请假开始,提交假单,然后领导审批,hr审批,ceo审批,只是不同的是,有些审批流程多了审核人或者是少了审核人,每种假单审核材料有所不同而已。
我们如何使用状态机来如何解决代码耦合性的问题,提高代码可扩展性可读性
如果我们把领导审批,hr审批,ceo审批,分别看做一个动作,每个相应都有几个状态,审批通过,不通过,拒绝,重新审核,会怎么样?

首先,我们将请假的类型定义成一个枚举:
public enum LeavePermitEnum {

    ANNUAL_LEAVE("annual_leave","年休假 "),
CASUAL_LEAVE("casual_leave","事假"),
MEDICAL_LEAVE("medical_leave","病假"),
MARRIAGE_LEAVE("marriage_leave","婚假"),; private String type;
private String memo;
//此处忽略构造方法和set/get方法
}

领导审批,hr审批,ceo审批,都有一个审批意见(通过,拒绝,或者是重修修改假单补充材料等),在这里,相当于一个事件Event,于是,整个状态扭转也可以用一个枚举类来表示,审批意见由一个枚举类Event来表示。

public enum Event {

    AGREE("agree","同意"),
DISSAGREE("disagree","不同意"),
MODIFY("modify","修改"),
;
private String type;
private String memo;
}

  因此,一个假单的状态就有很多种,用一个枚举代表整个假单的状态:

public enum Status {
//提交假单
PERMIT_SUBMIT("permitSubmit","提交假单"),
//领导审批
LEADER_PERMITING("leaderPermiting","领导审批中"),
LEADER_PERMIT_AGREE("leaderAgree","领导同意"),
LEADER_PERMIT_DISAGREE("leaderDisAgree","领导不同意"),
LEADER_PERMIT_MODIFY("leaderModify","领导觉得需要补充材料重修修改"), //hr审批
HR_PERMITING("hrPermiting","hr审批中"),
HR_PERMIT_AGREE("hrAgree","hr同意"),
HR_PERMIT_DISAGREE("hrDisAgree","hr不同意"),
HR_PERMIT_MODIFY("hrModify","hr觉得需要补充材料重修修改"),
//ceo审批
CEO_PERMITING("ceoPermiting","领导审批中"),
CEO_PERMIT_AGREE("ceoAgree","ceo同意"),
CEO_PERMIT_DISAGREE("ceoDisAgree","ceo不同意"),
CEO_PERMIT_MODIFY("ceoModify","ceo觉得需要补充材料重修修改"), //最终请假状态
PERMIT_SUCCESS("permitSuccess","请假成功"),
PERMIT_FAIL("permitFail","请假失败")
; private String status;
private String memo; private Status(String status,String memo){
this.status=status;
this.memo=memo;
}
}

  

状态定义清楚之后,需要考虑两个问题

  • 从当前状态需要能够跳转到下一个状态,比如提交假单之后,要能够从提交假单状态跳转到领导审批状态。
  • 不同的审批意见要能够跳转不同的状态,比如领导审批状态跳转审批通过,或者拒绝该审批需要能够按照Event状态跳转不同的状态。

这块功能可以交给状态机StatusMachine去解决,由当前状态+事件驱动(也就是当前请假的状态和审批意见)获取下一个状态。

我们知道,请假的种类不同,所走的流程也不同,相应的处理也不同,每种假单都有自己的审批链,也对应每种假单有不同的状态机,不难设计StatusMachine为接口或抽象类。状态机只做一件事情,根据event(审批意见),跳转下一个状态机。

public interface StatusMachine {
/**
*@params status 当前状态
*@params event 审批意见
*@return 下一个状态
**/
public Status getNextStatus(Status status,Event event);
}

  

这里举两个例子,一个病假,一个年休假的实现:

年休假的审批流程:

  • 提交假单 PERMIT_SUBMIT
  • 领导审批 LEADER_PERMITING
  • 等待领导审批
  • 领导审批通过/不通过/拒绝
  • 领导审批通过 LEADER_PERMIT_AGREE
  • ceo审批 CEO_PERMITING
  • 等待ceo审批意见
  • ceo审批通过/不通过/拒绝
  • ceo审批通过 CEO_PERMIT_AGREE
  • 请假完成 PERMIT_SUCCESS

因此事假的状态机StatusMachine实现如下:

public class AnnualLeaveStatusMachine implements StatusMachine{

    public Status getNextStatus(Status status,Event event){
switch (status){ case PERMIT_SUBMIT:
//提交假单状态无需审批跳转领导审批中状态
return Status.LEADER_PERMITING; case LEADER_PERMITING:
//领导审批需要审批意见 审批意见不用返回不同的状态
return getLeaderPermitStatus(event);
case LEADER_PERMIT_AGREE:
//领导同意请假,则跳转ceo审批
return Status.CEO_PERMITING;
case LEADER_PERMIT_DISAGREE:
//领导不同意该假单,则请假失败
return Status.PERMIT_FAIL;
case LEADER_PERMIT_MODIFY:
return getLeaderPermitStatus(event); case CEO_PERMITING:
//ceo审批需要审批意见
return getCEOPermitStatus(event);
case CEO_PERMIT_AGREE:
// ceo审批同意 跳转审批通过 请假完成
return Status.PERMIT_SUCCESS; case CEO_PERMIT_DISAGREE:
//ceo不同意审批 则跳转审批失败
return Status.PERMIT_FAIL;
case CEO_PERMIT_MODIFY:
return getCEOPermitStatus(event); default:
throw new RuntimeException("没有该流程");
}
} private Status getLeaderPermitStatus(Event event){
switch (event){
case AGREE:
//领导审批通过 返回同意该假单
return Status.LEADER_PERMIT_AGREE;
case DISSAGREE:
//领导不同意 则返回领导拒绝改假单状态
return Status.LEADER_PERMIT_DISAGREE;
case MODIFY:
return Status.LEADER_PERMIT_MODIFY;
default:
throw new RuntimeException("不支持该Event审批意见");
}
} private Status getCEOPermitStatus(Event event){
switch (event){
case AGREE:
//ceo审批通过 则返回ceo同意该假单
return Status.CEO_PERMIT_AGREE;
case DISSAGREE:
// ceo审批不通过 则返回ceo不同意该假单状态
return Status.CEO_PERMIT_DISAGREE;
case MODIFY:
return Status.CEO_PERMIT_MODIFY;
default:
throw new RuntimeException("不支持该Event审批意见");
}
}
}

  

病假的审批流程:

  • 提交假单 PERMIT_SUBMIT
  • 领导审批 LEADER_PERMITING
  • 等待领导审批
  • 领导审批通过/不通过/拒绝
  • 领导审批通过 LEADER_PERMIT_AGREE
  • HR审批 HR_PERMITING
  • 等待HR审批意见
  • HR审批通过/不通过/拒绝
  • HR审批通过 CEO_PERMIT_AGREE
  • 请假完成 PERMIT_SUCCESS
    根据该流程不难设计出该状态机
public class MedicalLeaveStatusMachine implements StatusMachine{

    public Status getNextStatus(Status status,Event event){
switch (status){
case PERMIT_SUBMIT:
//提交假单状态直接跳转领导审批中状态
return Status.LEADER_PERMITING; case LEADER_PERMITING:
//领导审批中状态需要审批意见再获取下一个状态
return getLeaderPermitStatus(event);
case LEADER_PERMIT_AGREE:
//领导同意审批该假单 跳转hr审批中状态
return Status.HR_PERMITING;
case LEADER_PERMIT_DISAGREE:
//领导不同意则返回请假失败
return Status.PERMIT_FAIL;
case LEADER_PERMIT_MODIFY:
return getLeaderPermitStatus(event); case HR_PERMITING:
//hr审批根据审批意见跳转下一个状态
return getHrPermitStatus(event);
case HR_PERMIT_AGREE:
//hr审批通过跳转审批完成状态
return Status.PERMIT_SUCCESS;
case HR_PERMIT_DISAGREE:
// hr审批不同意 返回请假失败
return Status.PERMIT_FAIL;
case HR_PERMIT_MODIFY:
return getHrPermitStatus(event); default:
throw new RuntimeException("没有该流程");
}
}
private Status getLeaderPermitStatus(Event event){
switch (event){
case AGREE:
//领导同意该假单,则返回领导审批通过
return Status.LEADER_PERMIT_AGREE;
case DISSAGREE:
//领导不同意该假单 则返回领导审批不通过
return Status.LEADER_PERMIT_DISAGREE;
case MODIFY:
return Status.LEADER_PERMIT_MODIFY;
default:
throw new RuntimeException("不支持该Event审批意见");
}
}
private Status getHrPermitStatus(Event event){
switch (event){
case AGREE:
//hr审批同意该假单,则返回hr同意状态
return Status.HR_PERMIT_AGREE;
case DISSAGREE:
//hr审批不同意该假单,则返回hr不同意状态
return Status.HR_PERMIT_DISAGREE;
case MODIFY:
return Status.HR_PERMIT_MODIFY;
default:
throw new RuntimeException("不支持该Event审批意见");
}
}
}

  对于请假的员工来讲,只知道提交了一个假单,并不会关心到底该流程怎么走,所以在设计的时候,需要根据请假类型能够自动匹配状态机,这里可以用静态工厂去实现。

public class StatusMachineFactory {

    private StatusMachineFactory(){

    }

    /**
* 根据状态获取状态机
* @param leavePermitType
* @return 对应请假类型的状态机
*/
public static StatusMachine getStatusMachine(LeavePermitType leavePermitType){
switch (leavePermitType){
case MEDICAL_LEAVE:
return new MedicalLeaveStatusMachine();
case ANNUAL_LEAVE:
return new AnnualLeaveStatusMachine();
default:
throw new RuntimeException("未知类型");
}
}
}

  

状态机设计好之后,每个状态都应该对应有该状态的处理类,且需要统一管理该状态和处理类的关系。
以年休假为例:提交假单->领导审批4个状态->ceo审批4个状态->请假完成/失败2个状态。

总计需要11个状态处理对象去处理该状态。

该状态处理类需要具备哪些能力

  • 处理该状态的业务
  • 能够决定要不要扭转该状态机接着往下走(提交假单状态处理结束要能够自动运行到领导审批状态,领导审批状态不能接着扭转到下一个状态,需要等待领导的审批意见才可继续往下走)

不难设计,先抽象出一个StatusHandler接口或父类,每个状态的处理类去实现该接口或继承该父类,在statusHandler中,有三个方法,before,dohandler,after,after主要负责扭转状态机,获取下一个状态的处理类处理下一个状态的事件。如果状态到达某一个状态不需要往下继续执行,则重写after方法即可中断状态机,dohandler主要负责做业务处理。


public interface AbstractStatusHandler {
public void handle(LeavePermit leavePermit);
} public abstract class StatusHandler implements AbstractStatusHandler{ protected void before(LeavePermit leavePermit){
} public void handle(LeavePermit leavePermit){
before(leavePermit);
doHandler(leavePermit);
after(leavePermit);
}
protected abstract void doHandler(LeavePermit leavePermit); protected void after(LeavePermit leavePermit){
//去下一个状态的处理对象处理
goNextStatusHandler(leavePermit);
} protected void goNextStatusHandler(LeavePermit leavePermit){
//获取下一个状态
leavePermit.setStatus(StatusMachineFactory.getStatusMachine(leavePermit.getLeavePermitType()).getNextStatus(leavePermit.getStatus(),leavePermit.getEvent()));
//状态机引擎驱动假单处理
StatusMachineEngine.post(leavePermit);
}

  在看一下具体的状态处理类实现,11个状态对应11个处理类,这里列举出部分

public class AnnualPermitSubmitStatusHandler extends StatusHandler{

    protected void doHandler(LeavePermit leavePermit){
System.out.println(String.format("user:%s--提交年休假假单--leavePermit status:%s",leavePermit.getUser(),leavePermit.getStatus().getStatus()));
} } public class AnnualLeaderPermitingStatusHandler extends StatusHandler{ protected void doHandler(LeavePermit leavePermit){
System.out.println(String.format("user:%s--领导审批年休假中--leavePermit status:%s",leavePermit.getUser(),leavePermit.getStatus().getStatus()));
}
@Override
protected void after(LeavePermit leavePermit){
if(leavePermit.getEvent()==null){
//还未审批,状态机结束,等待审批意见
System.out.println(String.format("user:%s--等待领导审批--leavePermit status:%s",leavePermit.getUser(),leavePermit.getStatus().getStatus()));
return;
}
super.goNextStatusHandler(leavePermit);
}
} public class AnnualLeaderAgreeStatusHandler extends StatusHandler{ protected void doHandler(LeavePermit leavePermit){
System.out.println(String.format("user:%s--直线领导同意请年休假--leavePermit status:%s",leavePermit.getUser(),leavePermit.getStatus().getStatus()));
} }
public class AnnualLeaderAgreeStatusHandler extends StatusHandler{ protected void doHandler(LeavePermit leavePermit){
leavePermit.setEvent(null);
System.out.println(String.format("user:%s--直线领导同意请年休假--leavePermit status:%s",leavePermit.getUser(),leavePermit.getStatus().getStatus()));
} } public class AnnualCEOPermitingStatusHandler extends StatusHandler{ protected void doHandler(LeavePermit leavePermit){
System.out.println(String.format("user:%s--ceo审批年休假中--leavePermit status:%s",leavePermit.getUser(),leavePermit.getStatus().getStatus())); } protected void after(LeavePermit leavePermit){
if(leavePermit.getEvent()==null){
//还未审批,状态机结束,等待审批意见
System.out.println(String.format("user:%s--等待ceo审批--leavePermit status:%s",leavePermit.getUser(),leavePermit.getStatus().getStatus()));
return;
}
goNextStatusHandler(leavePermit);
} }
public class AnnualCEOAgreeStatusHandler extends StatusHandler{ protected void doHandler(LeavePermit leavePermit){
System.out.println(String.format("user:%s--ceo同意休年休假--leavePermit status:%s",leavePermit.getUser(),leavePermit.getStatus().getStatus()));
} } public class AnnualPermitSuccessStatusHandler extends StatusHandler{ @Override
protected void doHandler(LeavePermit leavePermit){
System.out.println(String.format("user:%s--请年休假假成功--leavePermit status:%s",leavePermit.getUser(),leavePermit.getStatus().getStatus(),leavePermit.getStatus().getMemo()));
}
@Override
protected void after(LeavePermit leavePermit){
}
}

  关于假单的请求,都会由StatusMachineEngine.post(LeavePermit)去处理,这里是如何做到按照请假类型,和状态找到对应的statusHandler的?
这里是使用eventbus去实现(基于消息订阅发布模式实现)

public class StatusMachineEngine {

    private static EventBus eventBus;
static{
eventBus = new EventBus();
} /**
* 发布一条假单
* @param leavePermit
*/
public static void post(LeavePermit leavePermit) {
eventBus.post(leavePermit);
} /**
* 假单处理类
* @param statusLeavePermitHandler
*/
public static void addListener(LeavePermitHandler statusLeavePermitHandler) {
eventBus.register(statusLeavePermitHandler);
}
}

  所有假单的处理都会交给LeavePermitHandler去处理,这个对象里按照请假类型和请假状态做路由,选择不同的statusHandler处理业务逻辑。

public class LeavePermitHandler {

    //处理假单 注解代表可以接受到StatusMachineEngine发布的假单
@Subscribe
@AllowConcurrentEvents
public void handle(LeavePermit leavePermit){
//获取到状态处理类,然后去处理 handler为StatusHandler的入口
getStatusHandler(leavePermit).handle(leavePermit);
} /**
* 根据假单获取StatusHandler 状态处理对象
* @param leavePermit
* @return
*/
public static StatusHandler getStatusHandler(LeavePermit leavePermit){
return StatusHandlerRegistry.acquireStatusHandler(leavePermit.getLeavePermitType(),leavePermit.getStatus());
}
}

  所有的状态处理类都会保存在StatusHandlerRegistry对象中,该对象负责注册所有有关请假类型,状态和状态处理类的关系,每次都根据请假类型和状态去获取StatusHandler。

public class StatusHandlerRegistry {

    private static Map<String,StatusHandler> statusHandlerMap;

    static {
statusHandlerMap=new ConcurrentHashMap<String, StatusHandler>();
} private StatusHandlerRegistry(){ } private static String getKey(LeavePermitType leavePermitType,Status status){
return String.format("%s@-@%s",leavePermitType.getType(),status.name());
} /**
* 注册状态处理类
* @param leavePermitType 请假类型
* @param status 请假状态
* @param statusHandler 状态处理对象
*/
public static void registryStatusHandler(LeavePermitType leavePermitType,Status status,StatusHandler statusHandler){
statusHandlerMap.put(getKey(leavePermitType,status),statusHandler);
} /**
* 获取状态处理类
* @param leavePermitType 请假类型
* @param status 请假状态
* @return StatusHandler
*/
public static StatusHandler acquireStatusHandler(LeavePermitType leavePermitType,Status status){
return statusHandlerMap.get(getKey(leavePermitType,status));
}
}

  

所以,在我们项目启动中,将请假类型,请假状态和状态处理对象StatusHandler注册到StatusHandlerRegistry中,当LeavePermitHandler 处理类接收到StatusHandlerEngine.post()的假单的时候,可以根据请假类型和状态获取相应的处理类StatusHandler,做相应状态逻辑的处理,逻辑处理结束,是否继续状态机取决于statusHandler的after方法是否调用goNextStatusHandler(leavePermit);在调用goNextStatusHandler(leavePermit)的时候,会去状态机获取下一个状态,StatusHandlerEngine.post(leavePermit)将继续去获取处理类statusHandler,这个时候,应为leavePermit的状态已经发生变化,所以获取到的statusHandler已经发生变化。
看一下运行结果:
public static void main(String[] args) {
//注册年休假的状态和对应状态的处理类StatusHandler。
registryAnnualPermitStatusHandler();
//注册病假的状态和对应状态的处理类StatusHandler。
registryMedicalPermitStatusHandler(); LeavePermitHandler leavePermitHandler=new LeavePermitHandler();
//状态机引擎接受事件处理类
StatusMachineEngine.addListener(leavePermitHandler);
//生成假单
LeavePermit leavePermit=new LeavePermit();
leavePermit.setLeavePermitType(LeavePermitType.ANNUAL_LEAVE);
leavePermit.setStatus(Status.PERMIT_SUBMIT);
leavePermit.setUser("jettyrun");
//假单交给引擎去执行
StatusMachineEngine.post(leavePermit);
System.out.println("----- 分割线 代表假条需要领导审批了,领导给个通过意见,然后状态机接着走-------");
leavePermit.setEvent(Event.AGREE);
StatusMachineEngine.post(leavePermit);
System.out.println("----- 分割线 代表假条需要ceo审批了,ceo给个通过意见,然后状态机接着走-------");
leavePermit.setEvent(Event.AGREE);
StatusMachineEngine.post(leavePermit);
System.out.println("--->>>>>>>>>end<<<<<<<<-------"); } public static void registryAnnualPermitStatusHandler() { StatusHandlerRegistry.registryStatusHandler(LeavePermitType.ANNUAL_LEAVE, Status.PERMIT_SUBMIT, new AnnualPermitSubmitStatusHandler()); StatusHandlerRegistry.registryStatusHandler(LeavePermitType.ANNUAL_LEAVE, Status.LEADER_PERMIT_AGREE, new AnnualLeaderAgreeStatusHandler());
StatusHandlerRegistry.registryStatusHandler(LeavePermitType.ANNUAL_LEAVE, Status.LEADER_PERMIT_DISAGREE, new AnnualLeaderDisAgreeStatusHandler());
StatusHandlerRegistry.registryStatusHandler(LeavePermitType.ANNUAL_LEAVE, Status.LEADER_PERMIT_MODIFY, new AnnualLeaderPermitModifyStatusHandler());
StatusHandlerRegistry.registryStatusHandler(LeavePermitType.ANNUAL_LEAVE, Status.LEADER_PERMITING, new AnnualLeaderPermitingStatusHandler()); StatusHandlerRegistry.registryStatusHandler(LeavePermitType.ANNUAL_LEAVE, Status.CEO_PERMIT_AGREE, new AnnualCEOAgreeStatusHandler());
StatusHandlerRegistry.registryStatusHandler(LeavePermitType.ANNUAL_LEAVE, Status.CEO_PERMIT_DISAGREE, new AnnualCEODisAgreeStatusHandler());
StatusHandlerRegistry.registryStatusHandler(LeavePermitType.ANNUAL_LEAVE, Status.CEO_PERMIT_MODIFY, new AnnualCEOPermitModifyStatusHandler());
StatusHandlerRegistry.registryStatusHandler(LeavePermitType.ANNUAL_LEAVE, Status.CEO_PERMITING, new AnnualCEOPermitingStatusHandler()); StatusHandlerRegistry.registryStatusHandler(LeavePermitType.ANNUAL_LEAVE, Status.PERMIT_SUCCESS, new AnnualPermitSuccessStatusHandler());
StatusHandlerRegistry.registryStatusHandler(LeavePermitType.ANNUAL_LEAVE, Status.PERMIT_FAIL, new AnnualPermitFailStatusHandler());
} public static void registryMedicalPermitStatusHandler() { StatusHandlerRegistry.registryStatusHandler(LeavePermitType.MEDICAL_LEAVE, Status.PERMIT_SUBMIT, new MedicalPermitSubmitStatusHandler()); StatusHandlerRegistry.registryStatusHandler(LeavePermitType.MEDICAL_LEAVE, Status.LEADER_PERMIT_AGREE, new MedicalLeaderAgreeStatusHandler());
StatusHandlerRegistry.registryStatusHandler(LeavePermitType.MEDICAL_LEAVE, Status.LEADER_PERMIT_DISAGREE, new MedicalLeaderDisAgreeStatusHandler
());
StatusHandlerRegistry.registryStatusHandler(LeavePermitType.MEDICAL_LEAVE, Status.LEADER_PERMIT_MODIFY, new MedicalLeaderPermitModifyStatusHandler());
StatusHandlerRegistry.registryStatusHandler(LeavePermitType.MEDICAL_LEAVE, Status.LEADER_PERMITING, new MedicalLeaderPermitingStatusHandler()); StatusHandlerRegistry.registryStatusHandler(LeavePermitType.MEDICAL_LEAVE, Status.HR_PERMIT_AGREE, new MedicalHrAgreeStatusHandler());
StatusHandlerRegistry.registryStatusHandler(LeavePermitType.MEDICAL_LEAVE, Status.HR_PERMIT_DISAGREE, new MedicalHrDisAgreeStatusHandler());
StatusHandlerRegistry.registryStatusHandler(LeavePermitType.MEDICAL_LEAVE, Status.HR_PERMIT_MODIFY, new MedicalHrPermitModifyStatusHandler());
StatusHandlerRegistry.registryStatusHandler(LeavePermitType.MEDICAL_LEAVE, Status.HR_PERMITING, new MedicalHrPermitingStatusHandler()); StatusHandlerRegistry.registryStatusHandler(LeavePermitType.MEDICAL_LEAVE, Status.PERMIT_SUCCESS, new MedicalPermitSuccessStatusHandler());
StatusHandlerRegistry.registryStatusHandler(LeavePermitType.MEDICAL_LEAVE, Status.PERMIT_FAIL, new MedicalPermitFailStatusHandler());
}

  执行结果:

user:jettyrun--提交年休假假单--leavePermit status:permitSubmit
user:jettyrun--领导审批年休假中--leavePermit status:leaderPermiting
user:jettyrun--等待领导审批--leavePermit status:leaderPermiting
----- 分割线 代表假条需要领导审批了,领导给个通过意见,然后状态机接着走-------
user:jettyrun--领导审批年休假中--leavePermit status:leaderPermiting
user:jettyrun--直线领导同意请年休假--leavePermit status:leaderAgree
user:jettyrun--ceo审批年休假中--leavePermit status:ceoPermiting
user:jettyrun--等待ceo审批--leavePermit status:ceoPermiting
----- 分割线 代表假条需要领导审批了,ceo给个通过意见,然后状态机接着走-------
user:jettyrun--ceo审批年休假中--leavePermit status:ceoPermiting
user:jettyrun--ceo同意休年休假--leavePermit status:ceoAgree
user:jettyrun--请年休假假成功--leavePermit status:permitSuccess
--->>>>>>>>>end<<<<<<<<-------

  

可以看到,当需要领导,CEO审批假单的时候,状态机能够自动中断,领导,ceo同意了该请假请求leavePermit.setEvent(Event.AGREE);状态机就能够自动运行到最终状态permitSuccess。
这只是请年休假,再请一个病假

 LeavePermit leavePermit2=new LeavePermit();
leavePermit2.setLeavePermitType(LeavePermitType.MEDICAL_LEAVE);
leavePermit2.setStatus(Status.PERMIT_SUBMIT);
leavePermit2.setUser("jettyrun2");
StatusMachineEngine.post(leavePermit2); System.out.println("----- 分割线 代表假条需要领导审批了,领导给个通过意见,然后状态机接着走-------");
leavePermit2.setEvent(Event.AGREE);
StatusMachineEngine.post(leavePermit2); System.out.println("----- 分割线 代表假条需要hr审批了,hr给个通过意见,然后状态机接着走-------");
leavePermit2.setEvent(Event.AGREE);
StatusMachineEngine.post(leavePermit2);
System.out.println("--->>>>>>>>>end<<<<<<<<-------");

  

user:jettyrun2--病假提交--leavePermit status:permitSubmit-提交假单
user:jettyrun2--领导审批病假中--leavePermit status:leaderPermiting-领导审批中
user:jettyrun2--等待领导病假审批--leavePermit status:leaderPermiting-领导审批中
----- 分割线 代表假条需要领导审批了,领导给个通过意见,然后状态机接着走-------
user:jettyrun2--领导审批病假中--leavePermit status:leaderPermiting-领导审批中
user:jettyrun2--领导同意休病假--leavePermit status:leaderAgree-领导同意
user:jettyrun2--hr审批病假中--leavePermit status:hrPermiting-hr审批中
user:jettyrun2--等待hr审批--leavePermit status:hrPermiting
----- 分割线 代表假条需要hr审批了,hr给个通过意见,然后状态机接着走-------
user:jettyrun2--hr审批病假中--leavePermit status:hrPermiting-hr审批中
user:jettyrun2--hr同意休病假--leavePermit status:hrAgree-hr同意
user:jettyrun2--成功病假审批--leavePermit status:permitSuccess-请假成功
--->>>>>>>>>end<<<<<<<<-------

  

该状态机的设计思想有一部分借鉴公司的几个项目,一部分来源于当当elastic-job的源码解读心得。
源代码地址请点击我 github

 

 

EventBus-实现java状态机的更多相关文章

  1. EventBus使用详解(一)——初步使用EventBus

    一.概述 EventBus是一款针对Android优化的发布/订阅事件总线.主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间 ...

  2. EventBus中观察者模式的应用

    一 介绍 EventBus是一款安卓的开源消息传递框架,地址:https://github.com/greenrobot/EventBus android系统的消息传递非常复杂,比如activity和 ...

  3. [Java]如何制作一个WordCount-Plus的Plugin

    主类 每个Plugin都有一个主类实现了com.jihuayu.wordcount.api.Plugin接口,这个主类就是插件的路口. 获取命令介绍 可以通过向方法getCommandUsage的参数 ...

  4. springboot+kotlin+springcloud+java+grade+maven混编?

    springboot + maven + java vs springboot + gradle + kotlin 快速搭建:https://start.spring.io/ springclould ...

  5. Guava ---- EventBus事件驱动模型

    在软件开发过程中, 难免有信息的共享或者对象间的协作. 怎样让对象间信息共享高效, 而且耦合性低. 这是一个难题. 而耦合性高将带来编码改动牵一发而动全身的连锁效应. Spring的风靡正是由于攻克了 ...

  6. Java设计模式之(十二)——观察者模式

    1.什么是观察者模式? Define a one-to-many dependency between objects so that when one object changes state, a ...

  7. 管理订单状态,该上状态机吗?轻量级状态机COLA StateMachine保姆级入门教程

    前言 在平常的后端项目开发中,状态机模式的使用其实没有大家想象中那么常见,笔者之前由于不在电商领域工作,很少在业务代码中用状态机来管理各种状态,一般都是手动get/set状态值.去年笔者进入了电商领域 ...

  8. 记一次JVM故障排除

    今天,自己开发的事件驱动的java大规模爬虫程序上线了几个新任务后突然异常. 异常: 程序业务异常,经查看CPU利用率满,内存满,一直报OOM,目测有内存泄露.如下图所示,四核16G的内粗,CPU高达 ...

  9. BATJ等大厂最全经典面试题分享

    金九银十,又到了面试求职高峰期,最近有很多网友都在求大厂面试题.正好我之前电脑里面有这方面的整理,于是就发上来分享给大家. 这些题目是网友去百度.蚂蚁金服.小米.乐视.美团.58.猎豹.360.新浪. ...

随机推荐

  1. VS Code Python 全新发布!Jupyter Notebook 原生支持终于来了!

    VS Code Python 全新发布!Jupyter Notebook 原生支持终于来了! 北京时间 2019 年 10 月 9 日,微软发布了全新的 VS Code Python 插件,带来了众多 ...

  2. ActiveMQ消息过滤

    前言 ActiveMQ提供了一种机制,使用它,消息服务可根据消息选择器中的标准来执行消息过滤.生产者可在消息中放入应用程序特有的属性,而消费者可使用基于这些属性的选择标准来表明对消息是否感兴趣.这就简 ...

  3. javascript异步延时加载及判断是否已加载js/css文件

    <html> <head> <script type="text/javascript"> /**======================= ...

  4. 一篇关于for循环的简单题练习,

    package practice; public class Practice { public static void main(String[] args) { 7.      *     **  ...

  5. react-native底部导航栏实现

    react-native-tab-navigator实现: bottom.js代码如下: import React, {Component} from 'react'; import {StyleSh ...

  6. webpack3 打包

    1. 基于 webpack 3.0 2.步骤.说明 2.1 webpack 本地初始化.安装基本包 npm init         >  package.json npm i  webpack ...

  7. python-ssh-远程服务器+远程docker执行命令

    在python语言中实现远程服务器执行命令+远程dcoker执行命令 def ssh_exec_command(ip, username, password, cmd=None): "&qu ...

  8. Linux基础命令练习题(附答案)

    1.分别用cat \tac\nl三个命令查看文件/etc/ssh/sshd_config文件中的内容,并用自己的话总计出这三个文档操作命令的不同之处? [root@localhost ~]# cat ...

  9. Linux学习--第十天--bash脚本、用户自定义变量、环境变量、位置参数变量、预定义变量、标准输入输出、wc、history、dd、PS1

    shell简介 分为两种c shell 和b shell b shell:sh.ksh.Bash.psh.zsh: (Bash和sh兼容,linux基本shell是Bash) c shell:csh. ...

  10. 删除表A的记录时,Oracle 报错:“ORA-02292:违反完整约束条件(XXX.FKXXX)- 已找到子记录

    1.找到以”FKXXX“为外键的表A的子表,直接运行select a.constraint_name, a.table_name, b.constraint_name from user_constr ...