EventBus-實(shí)現(xiàn)java狀態(tài)機(jī)

首先,了解狀態(tài)機(jī)是什么,我們?yōu)槭裁葱枰獱顟B(tài)機(jī)!
舉個(gè)最簡(jiǎn)單例子,請(qǐng)假,作為一個(gè)最底層程序員,每次請(qǐng)假都要領(lǐng)導(dǎo)層層審批,而假有分為很多種,事假,病假,婚假,年休假等等,當(dāng)然選擇請(qǐng)的假不同,審批標(biāo)準(zhǔn)也不同,不同的假單需要走的審批鏈也不一樣,比如年休假,可能只需要領(lǐng)導(dǎo)審批扣掉年休假即可,請(qǐng)病假需要領(lǐng)導(dǎo)審批,領(lǐng)導(dǎo)審批之后,先休假,等休完假回來提交病假的材料,由hr審批之后才能完成整個(gè)請(qǐng)假過程。更有甚者,如果你要修一個(gè)一個(gè)月的長假,就不僅僅是需要直線領(lǐng)導(dǎo)hr審批,可能還需要公司ceo審批 ,審批通過后,才能通過。如下圖:


請(qǐng)假流程.png

當(dāng)然,實(shí)際來講,請(qǐng)假的種類和鏈路比這個(gè)要復(fù)雜的多,我們一般會(huì)怎么實(shí)現(xiàn),是否要使用if else了,對(duì)應(yīng)不同的假單,走不同的分支,代碼寫出來就成了一個(gè)非常復(fù)雜的,多級(jí)嵌套的代碼了,后面如何維護(hù)代碼,多了幾種假的種類,是不是又要if else了。如下代碼:

 public void requestLeavePermit(String type){
        if(type.equals("事假")){
            //領(lǐng)導(dǎo)審批->hr審批->ceo審批->完成
        }else if(type.equals("病假")){
              //領(lǐng)導(dǎo)審批->休假->補(bǔ)充病例->hr審批->完成
        }else if(type.equals("年休假")){
               //領(lǐng)導(dǎo)審批->hr審批->通過
        }else if(type.equals("產(chǎn)假")){
            //領(lǐng)導(dǎo)審批->hr審批->通過
        }else if(type.equals("調(diào)休假")){
              //領(lǐng)導(dǎo)審批->ceo審批->通過
        }
    }

或者寫成這個(gè)樣子:

 public void requestLeavePermit(String type,String userName){
        switch (type){
            case "事假":
                       //領(lǐng)導(dǎo)審批->hr審批->ceo審批->完成
                    break;
            case "病假":
                        //領(lǐng)導(dǎo)審批->休假->補(bǔ)充病例->hr審批->完成
                    break;
            case "年休假":
                        //領(lǐng)導(dǎo)審批->hr審批->通過
                    break;
            case "產(chǎn)假":
                      //領(lǐng)導(dǎo)審批->hr審批->通過
                    break;
            case "調(diào)休假":
                    //領(lǐng)導(dǎo)審批->ceo審批->通過
            default:
                    break;
        }
    }

if,else嵌套太深,然后每個(gè)if,else又是自己的處理流程,這樣代碼結(jié)構(gòu)會(huì)原來越復(fù)雜,當(dāng)審批鏈發(fā)生變更,這個(gè)時(shí)候會(huì)發(fā)現(xiàn)代碼耦合性太強(qiáng),導(dǎo)致修改起來很麻煩。
如何解決這個(gè)問題,我們不難看到,所有的請(qǐng)假都經(jīng)過了這樣幾個(gè)階段,從請(qǐng)假開始,提交假單,然后領(lǐng)導(dǎo)審批,hr審批,ceo審批,只是不同的是,有些審批流程多了審核人或者是少了審核人,每種假單審核材料有所不同而已。
我們?nèi)绾问褂脿顟B(tài)機(jī)來如何解決代碼耦合性的問題,提高代碼可擴(kuò)展性可讀性。
如果我們把領(lǐng)導(dǎo)審批,hr審批,ceo審批,分別看做一個(gè)動(dòng)作,每個(gè)相應(yīng)都有幾個(gè)狀態(tài),審批通過,不通過,拒絕,重新審核,會(huì)怎么樣?

帶狀態(tài)的流程

首先,我們將請(qǐng)假的類型定義成一個(gè)枚舉:

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;
    //此處忽略構(gòu)造方法和set/get方法
}

領(lǐng)導(dǎo)審批,hr審批,ceo審批,都有一個(gè)審批意見(通過,拒絕,或者是重修修改假單補(bǔ)充材料等),在這里,相當(dāng)于一個(gè)事件Event,于是,整個(gè)狀態(tài)扭轉(zhuǎn)也可以用一個(gè)枚舉類來表示,審批意見由一個(gè)枚舉類Event來表示。

public enum Event {

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

因此,一個(gè)假單的狀態(tài)就有很多種,用一個(gè)枚舉代表整個(gè)假單的狀態(tài):

public enum Status {
    //提交假單
    PERMIT_SUBMIT("permitSubmit","提交假單"),
     //領(lǐng)導(dǎo)審批
    LEADER_PERMITING("leaderPermiting","領(lǐng)導(dǎo)審批中"),
    LEADER_PERMIT_AGREE("leaderAgree","領(lǐng)導(dǎo)同意"),
    LEADER_PERMIT_DISAGREE("leaderDisAgree","領(lǐng)導(dǎo)不同意"),
    LEADER_PERMIT_MODIFY("leaderModify","領(lǐng)導(dǎo)覺得需要補(bǔ)充材料重修修改"),

    //hr審批
    HR_PERMITING("hrPermiting","hr審批中"),
    HR_PERMIT_AGREE("hrAgree","hr同意"),
    HR_PERMIT_DISAGREE("hrDisAgree","hr不同意"),
    HR_PERMIT_MODIFY("hrModify","hr覺得需要補(bǔ)充材料重修修改"),
    //ceo審批
    CEO_PERMITING("ceoPermiting","領(lǐng)導(dǎo)審批中"),
    CEO_PERMIT_AGREE("ceoAgree","ceo同意"),
    CEO_PERMIT_DISAGREE("ceoDisAgree","ceo不同意"),
    CEO_PERMIT_MODIFY("ceoModify","ceo覺得需要補(bǔ)充材料重修修改"),

    //最終請(qǐng)假狀態(tài)
    PERMIT_SUCCESS("permitSuccess","請(qǐng)假成功"),
    PERMIT_FAIL("permitFail","請(qǐng)假失敗")
    ;

    private String status;
    private String memo;

    private Status(String status,String memo){
        this.status=status;
        this.memo=memo;
    }
}

狀態(tài)定義清楚之后,需要考慮兩個(gè)問題

  • 從當(dāng)前狀態(tài)需要能夠跳轉(zhuǎn)到下一個(gè)狀態(tài),比如提交假單之后,要能夠從提交假單狀態(tài)跳轉(zhuǎn)到領(lǐng)導(dǎo)審批狀態(tài)。
  • 不同的審批意見要能夠跳轉(zhuǎn)不同的狀態(tài),比如領(lǐng)導(dǎo)審批狀態(tài)跳轉(zhuǎn)審批通過,或者拒絕該審批需要能夠按照Event狀態(tài)跳轉(zhuǎn)不同的狀態(tài)。

這塊功能可以交給狀態(tài)機(jī)StatusMachine去解決,由當(dāng)前狀態(tài)+事件驅(qū)動(dòng)(也就是當(dāng)前請(qǐng)假的狀態(tài)和審批意見)獲取下一個(gè)狀態(tài)。

我們知道,請(qǐng)假的種類不同,所走的流程也不同,相應(yīng)的處理也不同,每種假單都有自己的審批鏈,也對(duì)應(yīng)每種假單有不同的狀態(tài)機(jī),不難設(shè)計(jì)StatusMachine為接口或抽象類。狀態(tài)機(jī)只做一件事情,根據(jù)event(審批意見),跳轉(zhuǎn)下一個(gè)狀態(tài)機(jī)。

public interface StatusMachine {
      /**
          *@params status 當(dāng)前狀態(tài)
          *@params event 審批意見
          *@return 下一個(gè)狀態(tài)
       **/
     public Status getNextStatus(Status status,Event event);
}

這里舉兩個(gè)例子,一個(gè)病假,一個(gè)年休假的實(shí)現(xiàn):

年休假的審批流程:

  • 提交假單 PERMIT_SUBMIT
  • 領(lǐng)導(dǎo)審批 LEADER_PERMITING
  • 等待領(lǐng)導(dǎo)審批
  • 領(lǐng)導(dǎo)審批通過/不通過/拒絕
  • 領(lǐng)導(dǎo)審批通過 LEADER_PERMIT_AGREE
  • ceo審批 CEO_PERMITING
  • 等待ceo審批意見
  • ceo審批通過/不通過/拒絕
  • ceo審批通過 CEO_PERMIT_AGREE
  • 請(qǐng)假完成 PERMIT_SUCCESS

因此事假的狀態(tài)機(jī)StatusMachine實(shí)現(xiàn)如下:

public class AnnualLeaveStatusMachine implements StatusMachine{


    public Status getNextStatus(Status status,Event event){
        switch (status){

            case PERMIT_SUBMIT:
                //提交假單狀態(tài)無需審批跳轉(zhuǎn)領(lǐng)導(dǎo)審批中狀態(tài)
                return Status.LEADER_PERMITING;

            case LEADER_PERMITING:
                //領(lǐng)導(dǎo)審批需要審批意見 審批意見不用返回不同的狀態(tài)
                return getLeaderPermitStatus(event);
            case LEADER_PERMIT_AGREE:
                //領(lǐng)導(dǎo)同意請(qǐng)假,則跳轉(zhuǎn)ceo審批
                return Status.CEO_PERMITING;
            case LEADER_PERMIT_DISAGREE:
                //領(lǐng)導(dǎo)不同意該假單,則請(qǐng)假失敗
                return Status.PERMIT_FAIL;
            case LEADER_PERMIT_MODIFY:
                return getLeaderPermitStatus(event);

            case CEO_PERMITING:
                //ceo審批需要審批意見
                return getCEOPermitStatus(event);
            case CEO_PERMIT_AGREE:
                // ceo審批同意 跳轉(zhuǎn)審批通過 請(qǐng)假完成
                return Status.PERMIT_SUCCESS;
            
            case CEO_PERMIT_DISAGREE:
                //ceo不同意審批 則跳轉(zhuǎn)審批失敗
                return Status.PERMIT_FAIL;
            case CEO_PERMIT_MODIFY:
                return  getCEOPermitStatus(event);

            default:
                throw new RuntimeException("沒有該流程");
        }
    }



    private Status getLeaderPermitStatus(Event event){
        switch (event){
            case AGREE:
                //領(lǐng)導(dǎo)審批通過 返回同意該假單
                return Status.LEADER_PERMIT_AGREE;
            case DISSAGREE:
                //領(lǐng)導(dǎo)不同意 則返回領(lǐng)導(dǎo)拒絕改假單狀態(tài)
                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不同意該假單狀態(tài)
                return Status.CEO_PERMIT_DISAGREE;
            case MODIFY:
                return Status.CEO_PERMIT_MODIFY;
            default:
                throw new RuntimeException("不支持該Event審批意見");
        }
    }
}

病假的審批流程:

  • 提交假單 PERMIT_SUBMIT
  • 領(lǐng)導(dǎo)審批 LEADER_PERMITING
  • 等待領(lǐng)導(dǎo)審批
  • 領(lǐng)導(dǎo)審批通過/不通過/拒絕
  • 領(lǐng)導(dǎo)審批通過 LEADER_PERMIT_AGREE
  • HR審批 HR_PERMITING
  • 等待HR審批意見
  • HR審批通過/不通過/拒絕
  • HR審批通過 CEO_PERMIT_AGREE
  • 請(qǐng)假完成 PERMIT_SUCCESS
    根據(jù)該流程不難設(shè)計(jì)出該狀態(tài)機(jī)
public class MedicalLeaveStatusMachine implements StatusMachine{

    public Status getNextStatus(Status status,Event event){
        switch (status){
            case PERMIT_SUBMIT:
                    //提交假單狀態(tài)直接跳轉(zhuǎn)領(lǐng)導(dǎo)審批中狀態(tài)
                    return Status.LEADER_PERMITING;


            case LEADER_PERMITING:
                    //領(lǐng)導(dǎo)審批中狀態(tài)需要審批意見再獲取下一個(gè)狀態(tài)
                    return getLeaderPermitStatus(event);
            case LEADER_PERMIT_AGREE:
                    //領(lǐng)導(dǎo)同意審批該假單 跳轉(zhuǎn)hr審批中狀態(tài)
                    return Status.HR_PERMITING; 
            case LEADER_PERMIT_DISAGREE:
                    //領(lǐng)導(dǎo)不同意則返回請(qǐng)假失敗
                    return Status.PERMIT_FAIL;
            case LEADER_PERMIT_MODIFY:
                    return  getLeaderPermitStatus(event);

            case HR_PERMITING:
                //hr審批根據(jù)審批意見跳轉(zhuǎn)下一個(gè)狀態(tài)
                return getHrPermitStatus(event);
            case HR_PERMIT_AGREE:
                //hr審批通過跳轉(zhuǎn)審批完成狀態(tài)
                return Status.PERMIT_SUCCESS;
            case HR_PERMIT_DISAGREE:
                // hr審批不同意 返回請(qǐng)假失敗
                return Status.PERMIT_FAIL;
            case HR_PERMIT_MODIFY:
                return  getHrPermitStatus(event);

            default:
                throw new RuntimeException("沒有該流程");
        }
    }
    private Status getLeaderPermitStatus(Event event){
        switch (event){
            case AGREE:
                //領(lǐng)導(dǎo)同意該假單,則返回領(lǐng)導(dǎo)審批通過
                return Status.LEADER_PERMIT_AGREE;
            case DISSAGREE:
                //領(lǐng)導(dǎo)不同意該假單 則返回領(lǐng)導(dǎo)審批不通過
                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同意狀態(tài)
                return Status.HR_PERMIT_AGREE;
            case DISSAGREE:
                //hr審批不同意該假單,則返回hr不同意狀態(tài)
                return Status.HR_PERMIT_DISAGREE;
            case MODIFY:
                return Status.HR_PERMIT_MODIFY;
            default:
                throw new RuntimeException("不支持該Event審批意見");
        }
    }
}

對(duì)于請(qǐng)假的員工來講,只知道提交了一個(gè)假單,并不會(huì)關(guān)心到底該流程怎么走,所以在設(shè)計(jì)的時(shí)候,需要根據(jù)請(qǐng)假類型能夠自動(dòng)匹配狀態(tài)機(jī),這里可以用靜態(tài)工廠去實(shí)現(xiàn)。

public class StatusMachineFactory {

    private StatusMachineFactory(){

    }

    /**
     * 根據(jù)狀態(tài)獲取狀態(tài)機(jī)
     * @param leavePermitType
     * @return 對(duì)應(yīng)請(qǐng)假類型的狀態(tài)機(jī)
     */
    public static StatusMachine getStatusMachine(LeavePermitType leavePermitType){
        switch (leavePermitType){
            case MEDICAL_LEAVE:
                return new MedicalLeaveStatusMachine();
            case ANNUAL_LEAVE:
                return new AnnualLeaveStatusMachine();
            default:
                throw new RuntimeException("未知類型");
        }
    }
}

狀態(tài)機(jī)設(shè)計(jì)好之后,每個(gè)狀態(tài)都應(yīng)該對(duì)應(yīng)有該狀態(tài)的處理類,且需要統(tǒng)一管理該狀態(tài)和處理類的關(guān)系。
以年休假為例:提交假單->領(lǐng)導(dǎo)審批4個(gè)狀態(tài)->ceo審批4個(gè)狀態(tài)->請(qǐng)假完成/失敗2個(gè)狀態(tài)。

總計(jì)需要11個(gè)狀態(tài)處理對(duì)象去處理該狀態(tài)。

該狀態(tài)處理類需要具備哪些能力

  • 處理該狀態(tài)的業(yè)務(wù)
  • 能夠決定要不要扭轉(zhuǎn)該狀態(tài)機(jī)接著往下走(提交假單狀態(tài)處理結(jié)束要能夠自動(dòng)運(yùn)行到領(lǐng)導(dǎo)審批狀態(tài),領(lǐng)導(dǎo)審批狀態(tài)不能接著扭轉(zhuǎn)到下一個(gè)狀態(tài),需要等待領(lǐng)導(dǎo)的審批意見才可繼續(xù)往下走)

不難設(shè)計(jì),先抽象出一個(gè)StatusHandler接口或父類,每個(gè)狀態(tài)的處理類去實(shí)現(xiàn)該接口或繼承該父類,在statusHandler中,有三個(gè)方法,before,dohandler,after,after主要負(fù)責(zé)扭轉(zhuǎn)狀態(tài)機(jī),獲取下一個(gè)狀態(tài)的處理類處理下一個(gè)狀態(tài)的事件。如果狀態(tài)到達(dá)某一個(gè)狀態(tài)不需要往下繼續(xù)執(zhí)行,則重寫after方法即可中斷狀態(tài)機(jī),dohandler主要負(fù)責(zé)做業(yè)務(wù)處理。


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){
        //去下一個(gè)狀態(tài)的處理對(duì)象處理
        goNextStatusHandler(leavePermit);
    }

    protected void goNextStatusHandler(LeavePermit leavePermit){
        //獲取下一個(gè)狀態(tài)
        leavePermit.setStatus(StatusMachineFactory.getStatusMachine(leavePermit.getLeavePermitType()).getNextStatus(leavePermit.getStatus(),leavePermit.getEvent()));
        //狀態(tài)機(jī)引擎驅(qū)動(dòng)假單處理
        StatusMachineEngine.post(leavePermit);
    }

在看一下具體的狀態(tài)處理類實(shí)現(xiàn),11個(gè)狀態(tài)對(duì)應(yīng)11個(gè)處理類,這里列舉出部分

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--領(lǐng)導(dǎo)審批年休假中--leavePermit status:%s",leavePermit.getUser(),leavePermit.getStatus().getStatus()));
    }
    @Override
    protected void after(LeavePermit leavePermit){
        if(leavePermit.getEvent()==null){
            //還未審批,狀態(tài)機(jī)結(jié)束,等待審批意見
            System.out.println(String.format("user:%s--等待領(lǐng)導(dǎo)審批--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--直線領(lǐng)導(dǎo)同意請(qǐng)年休假--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--直線領(lǐng)導(dǎo)同意請(qǐng)年休假--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){
           //還未審批,狀態(tài)機(jī)結(jié)束,等待審批意見
           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--請(qǐng)年休假假成功--leavePermit status:%s",leavePermit.getUser(),leavePermit.getStatus().getStatus(),leavePermit.getStatus().getMemo()));
    }
    @Override
    protected void after(LeavePermit leavePermit){
    }
}

關(guān)于假單的請(qǐng)求,都會(huì)由StatusMachineEngine.post(LeavePermit)去處理,這里是如何做到按照請(qǐng)假類型,和狀態(tài)找到對(duì)應(yīng)的statusHandler的?
這里是使用eventbus去實(shí)現(xiàn)(基于消息訂閱發(fā)布模式實(shí)現(xiàn))

public class StatusMachineEngine {

    private static EventBus eventBus;
    static{
        eventBus = new EventBus();
    }

    /**
     * 發(fā)布一條假單
     * @param leavePermit
     */
    public static void post(LeavePermit leavePermit) {
        eventBus.post(leavePermit);
    }

    /**
     * 假單處理類
     * @param statusLeavePermitHandler
     */
    public static void addListener(LeavePermitHandler statusLeavePermitHandler) {
        eventBus.register(statusLeavePermitHandler);
    }
}

所有假單的處理都會(huì)交給LeavePermitHandler去處理,這個(gè)對(duì)象里按照請(qǐng)假類型和請(qǐng)假狀態(tài)做路由,選擇不同的statusHandler處理業(yè)務(wù)邏輯。

public class LeavePermitHandler {

    //處理假單 注解代表可以接受到StatusMachineEngine發(fā)布的假單
    @Subscribe
    @AllowConcurrentEvents
    public void handle(LeavePermit leavePermit){
        //獲取到狀態(tài)處理類,然后去處理 handler為StatusHandler的入口
        getStatusHandler(leavePermit).handle(leavePermit);
    }

    /**
     * 根據(jù)假單獲取StatusHandler 狀態(tài)處理對(duì)象
     * @param leavePermit
     * @return
     */
    public static StatusHandler getStatusHandler(LeavePermit leavePermit){
        return StatusHandlerRegistry.acquireStatusHandler(leavePermit.getLeavePermitType(),leavePermit.getStatus());
    }
}

所有的狀態(tài)處理類都會(huì)保存在StatusHandlerRegistry對(duì)象中,該對(duì)象負(fù)責(zé)注冊(cè)所有有關(guān)請(qǐng)假類型,狀態(tài)和狀態(tài)處理類的關(guān)系,每次都根據(jù)請(qǐng)假類型和狀態(tài)去獲取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());
    }

    /**
     * 注冊(cè)狀態(tài)處理類
     * @param leavePermitType  請(qǐng)假類型
     * @param status           請(qǐng)假狀態(tài)
     * @param statusHandler    狀態(tài)處理對(duì)象
     */
    public static void registryStatusHandler(LeavePermitType leavePermitType,Status status,StatusHandler statusHandler){
        statusHandlerMap.put(getKey(leavePermitType,status),statusHandler);
    }

    /**
     * 獲取狀態(tài)處理類
     * @param leavePermitType  請(qǐng)假類型
     * @param status            請(qǐng)假狀態(tài)
     * @return StatusHandler         
     */
    public static StatusHandler acquireStatusHandler(LeavePermitType leavePermitType,Status status){
        return statusHandlerMap.get(getKey(leavePermitType,status));
    }
}

所以,在我們項(xiàng)目啟動(dòng)中,將請(qǐng)假類型,請(qǐng)假狀態(tài)和狀態(tài)處理對(duì)象StatusHandler注冊(cè)到StatusHandlerRegistry中,當(dāng)LeavePermitHandler 處理類接收到StatusHandlerEngine.post()的假單的時(shí)候,可以根據(jù)請(qǐng)假類型和狀態(tài)獲取相應(yīng)的處理類StatusHandler,做相應(yīng)狀態(tài)邏輯的處理,邏輯處理結(jié)束,是否繼續(xù)狀態(tài)機(jī)取決于statusHandler的after方法是否調(diào)用goNextStatusHandler(leavePermit);在調(diào)用goNextStatusHandler(leavePermit)的時(shí)候,會(huì)去狀態(tài)機(jī)獲取下一個(gè)狀態(tài),StatusHandlerEngine.post(leavePermit)將繼續(xù)去獲取處理類statusHandler,這個(gè)時(shí)候,應(yīng)為leavePermit的狀態(tài)已經(jīng)發(fā)生變化,所以獲取到的statusHandler已經(jīng)發(fā)生變化。
看一下運(yùn)行結(jié)果:

 public static void main(String[] args) {
       //注冊(cè)年休假的狀態(tài)和對(duì)應(yīng)狀態(tài)的處理類StatusHandler。
        registryAnnualPermitStatusHandler();
        //注冊(cè)病假的狀態(tài)和對(duì)應(yīng)狀態(tài)的處理類StatusHandler。
        registryMedicalPermitStatusHandler();

        LeavePermitHandler leavePermitHandler=new LeavePermitHandler();
        //狀態(tài)機(jī)引擎接受事件處理類
        StatusMachineEngine.addListener(leavePermitHandler);
        //生成假單
        LeavePermit leavePermit=new LeavePermit();
        leavePermit.setLeavePermitType(LeavePermitType.ANNUAL_LEAVE);
        leavePermit.setStatus(Status.PERMIT_SUBMIT);
        leavePermit.setUser("jettyrun");
        //假單交給引擎去執(zhí)行
        StatusMachineEngine.post(leavePermit);
        System.out.println("----- 分割線 代表假條需要領(lǐng)導(dǎo)審批了,領(lǐng)導(dǎo)給個(gè)通過意見,然后狀態(tài)機(jī)接著走-------");
        leavePermit.setEvent(Event.AGREE);
        StatusMachineEngine.post(leavePermit);
        System.out.println("----- 分割線 代表假條需要ceo審批了,ceo給個(gè)通過意見,然后狀態(tài)機(jī)接著走-------");
        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());
    }

執(zhí)行結(jié)果:

user:jettyrun--提交年休假假單--leavePermit status:permitSubmit
user:jettyrun--領(lǐng)導(dǎo)審批年休假中--leavePermit status:leaderPermiting
user:jettyrun--等待領(lǐng)導(dǎo)審批--leavePermit status:leaderPermiting
----- 分割線 代表假條需要領(lǐng)導(dǎo)審批了,領(lǐng)導(dǎo)給個(gè)通過意見,然后狀態(tài)機(jī)接著走-------
user:jettyrun--領(lǐng)導(dǎo)審批年休假中--leavePermit status:leaderPermiting
user:jettyrun--直線領(lǐng)導(dǎo)同意請(qǐng)年休假--leavePermit status:leaderAgree
user:jettyrun--ceo審批年休假中--leavePermit status:ceoPermiting
user:jettyrun--等待ceo審批--leavePermit status:ceoPermiting
----- 分割線 代表假條需要領(lǐng)導(dǎo)審批了,ceo給個(gè)通過意見,然后狀態(tài)機(jī)接著走-------
user:jettyrun--ceo審批年休假中--leavePermit status:ceoPermiting
user:jettyrun--ceo同意休年休假--leavePermit status:ceoAgree
user:jettyrun--請(qǐng)年休假假成功--leavePermit status:permitSuccess
--->>>>>>>>>end<<<<<<<<-------

可以看到,當(dāng)需要領(lǐng)導(dǎo),CEO審批假單的時(shí)候,狀態(tài)機(jī)能夠自動(dòng)中斷,領(lǐng)導(dǎo),ceo同意了該請(qǐng)假請(qǐng)求leavePermit.setEvent(Event.AGREE);狀態(tài)機(jī)就能夠自動(dòng)運(yùn)行到最終狀態(tài)permitSuccess。
這只是請(qǐng)年休假,再請(qǐng)一個(gè)病假

        LeavePermit leavePermit2=new LeavePermit();
        leavePermit2.setLeavePermitType(LeavePermitType.MEDICAL_LEAVE);
        leavePermit2.setStatus(Status.PERMIT_SUBMIT);
        leavePermit2.setUser("jettyrun2");
        StatusMachineEngine.post(leavePermit2);

        System.out.println("----- 分割線 代表假條需要領(lǐng)導(dǎo)審批了,領(lǐng)導(dǎo)給個(gè)通過意見,然后狀態(tài)機(jī)接著走-------");
        leavePermit2.setEvent(Event.AGREE);
        StatusMachineEngine.post(leavePermit2);


        System.out.println("----- 分割線 代表假條需要hr審批了,hr給個(gè)通過意見,然后狀態(tài)機(jī)接著走-------");
        leavePermit2.setEvent(Event.AGREE);
        StatusMachineEngine.post(leavePermit2);
        System.out.println("--->>>>>>>>>end<<<<<<<<-------");
user:jettyrun2--病假提交--leavePermit status:permitSubmit-提交假單
user:jettyrun2--領(lǐng)導(dǎo)審批病假中--leavePermit status:leaderPermiting-領(lǐng)導(dǎo)審批中
user:jettyrun2--等待領(lǐng)導(dǎo)病假審批--leavePermit status:leaderPermiting-領(lǐng)導(dǎo)審批中
----- 分割線 代表假條需要領(lǐng)導(dǎo)審批了,領(lǐng)導(dǎo)給個(gè)通過意見,然后狀態(tài)機(jī)接著走-------
user:jettyrun2--領(lǐng)導(dǎo)審批病假中--leavePermit status:leaderPermiting-領(lǐng)導(dǎo)審批中
user:jettyrun2--領(lǐng)導(dǎo)同意休病假--leavePermit status:leaderAgree-領(lǐng)導(dǎo)同意
user:jettyrun2--hr審批病假中--leavePermit status:hrPermiting-hr審批中
user:jettyrun2--等待hr審批--leavePermit status:hrPermiting
----- 分割線 代表假條需要hr審批了,hr給個(gè)通過意見,然后狀態(tài)機(jī)接著走-------
user:jettyrun2--hr審批病假中--leavePermit status:hrPermiting-hr審批中
user:jettyrun2--hr同意休病假--leavePermit status:hrAgree-hr同意
user:jettyrun2--成功病假審批--leavePermit status:permitSuccess-請(qǐng)假成功
--->>>>>>>>>end<<<<<<<<-------

該狀態(tài)機(jī)的設(shè)計(jì)思想有一部分借鑒公司的幾個(gè)項(xiàng)目,一部分來源于當(dāng)當(dāng)elastic-job的源碼解讀心得。
源代碼地址請(qǐng)點(diǎn)擊我 github
fyi

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,577評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,600評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,944評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,108評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,652評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,385評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,616評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,798評(píng)論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評(píng)論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,334評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,570評(píng)論 2 379