EventBus-实现java状态机
发布时间:2023-03-25 14:01:48 所属栏目:教程 来源:
导读:当然,实际来讲,请假的种类和链路比这个要复杂的多,我们一般会怎么实现,是否要使用if else了,对应不同的假单,走不同的分支,代码写出来就成了一个非常复杂的,多级嵌套的代码了,后面如何维护代码,多了几种假的
当然,实际来讲,请假的种类和链路比这个要复杂的多,我们一般会怎么实现,是否要使用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个状态。 处理该状态的业务 能够决定要不要扭转该状态机接着往下走(提交假单状态处理结束要能够自动运行到领导审批状态,领导审批状态不能接着扭转到下一个状态,需要等待领导的审批意见才可继续往下走) 不难设计,先抽象出一个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.getStatus().getStatus())); } @Override protected void after(LeavePermit leavePermit){ if(leavePermit.getEvent()==null){ //还未审批,状态机结束,等待审批意见 System.out.println(String.format("user:%s--等待领导审批--leavePermit status:%s",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.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.getStatus().getStatus())); } } public class AnnualCEOPermitingStatusHandler extends StatusHandler{ protected void doHandler(LeavePermit leavePermit){ System.out.println(String.format("user:%s--ceo审批年休假中--leavePermit status:%s",leavePermit.getStatus().getStatus())); } protected void after(LeavePermit leavePermit){ if(leavePermit.getEvent()==null){ //还未审批,状态机结束,等待审批意见 System.out.println(String.format("user:%s--等待ceo审批--leavePermit status:%s",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.getStatus().getStatus())); } } public class AnnualPermitSuccessstatusHandler extends StatusHandler{ @Override protected void doHandler(LeavePermit leavePermit){ System.out.println(String.format("user:%s--请年休假假成功--leavePermit status:%s",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<<<<<<<<-------"); (编辑:汽车网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
推荐文章
站长推荐