1. 程式人生 > 實用技巧 >EventBus-實現java狀態機

EventBus-實現java狀態機

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

首先,瞭解狀態機是什麼,我們為什麼需要狀態機!
舉個最簡單例子,請假,作為一個最底層程式設計師,每次請假都要領導層層審批,而假有分為很多種,事假,病假,婚假,年休假等等,當然選擇請的假不同,審批標準也不同,不同的假單需要走的審批鏈也不一樣,比如年休假,可能只需要領導審批扣掉年休假即可,請病假需要領導審批,領導審批之後,先休假,等休完假回來提交病假的材料,由hr審批之後才能完成整個請假過程。更有甚者,如果你要修一個一個月的長假,就不僅僅是需要直線領導hr審批,可能還需要公司ceo審批 ,審批通過後,才能通過。如下圖:

請假流程.png

當然,實際來講,請假的種類和鏈路比這個要複雜的多,我們一般會怎麼實現,是否要使用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