业务背景最近接到公司的一个小请求,需要扩充现有的试用用户申请规则。我们的场景大致是这样的:if(是否是海外用户){returnfalse;}if(刷单的客户){returnfalse;}if(未付费用户&&不再服务期){returnfalse;}if(转介user||Paiduser||referreduser){returntrue;}else{returnfalse;}根据以上条件,我们可以得出结论,我们的主要流程主要是基于and或or的关系。如果不匹配,其实我们后面的流程就不用执行了,只是需要一个短路函数。对于现在的现状,如果我在原来的基础上修改,只要稍加注意,解决需求问题不大,但是后期可维护性很差。后来权衡了一下,决定重构这部分。针对这个需求,我首先梳理了我们规则执行器的总体设计。我们首先需要对规则进行抽象,然后定义规则模板,然后自己使用规则模板来实现具体的规则。最后,对于可能存在共享对象转换的规则,我们可以提前在模板方法中定义。后期如果需要的话,我们可以通过添加DSL语言或者脚本语言解析器,反射类文件的方式来实现动态扩展。最后设计了一个V1版本分享给大家。如果你有这样的案例,可以分享给我,也可以留言。下面主要介绍设计实现过程和代码。规则执行器的设计是针对我的规则执行器的设计,我的灵感来自<>和<>。在这种场景下,我们首先想到的是将规则的自然语言转化为程序代码。在DDD设计中,我们可以选择一种DSL方式来处理Rule;对于业务数据处理或者其他复杂的流程,我们可以通过Rule模板来定制和实现特定的Rule策略。规则执行器的处理步骤如下:首先,需要构建业务数据,如用户基础,用户状态,以及一些业务数据;然后通过当前上下文获取具体规则列表,可以从规则工厂获取;然后调用规则执行方法得到结果。在执行过程中,对于链接关系的处理,常用的关系包括与或否等抽象规则,以及定义模板。首先需要将BaseRule定义为Rule的抽象,将execute方法定义为执行方法。然后将AbstractRule定义为一个规则模板,作为一个方法的公共实现,并提供扩展点convert和executeRule来允许用户将自定义的RuleD转换为数据结构。AddressRule和NationalityRule分别作为两个具体的策略或者具体的实现来实现Rule。//业务数据@DatapublicclassRuleDto{privateStringaddress;privateintage;}//规则抽像publicinterfaceBaseRule{booleanexecute(RuleDtodto);}//规则模板publicabstractclassAbstractRuleimplementsBaseRule{protectedTconvert(RuleDtodto){return}publicinterfaceBaseRule(RuleDtodto);){returnexecuteRule(convert(dto));}protectedbooleanexecuteRule(Tt){returntrue;}}//实体规则-示例1publicclassAddressRuleextendsAbstractRule{@Overridepublicbooleanexecute(RuleDtodto){System.out.println("AddressRuleinvoke!");if(dto.getAddress().startsWith(MATCH_ADDRESS_START)){returntrue;}returnfalse;}}//具体规则-例子2publicclassNationalityRuleextendsAbstractRule{@OverrideprotectedTconvert(RuleDtodto){NationalityRuleDtonationalityRuleDto=newNationalityRuleDto();if(dto.getAddress().startsWith(MATCH_ADDRESS_START)){nationalityRuleDto.setNationality(MATCH_NATIONALITY_START);}return(T)nationalityRuleDto;}@OverrideprotectedbooleanexecuteRule(Tt){System.out.println("NationalityRuleinvoke!");NationalityRuleDtonationalityRuleDto=(NationalityRuleDto)t;if(nationalityRuleDto.getNationality().startsWith(MATCH_NATIONALITY_START)){returntrue;}returnfalse;}}//常量定义publicclassRuleConstant{publicstaticfinalStringMATCH_ADDRESS_START=ALCHATICN;PublicstaticStringNSTART=北京"China";}规则执行器RuleService的核心构建是规则执行和规则管道链接的具体类。在这个类中,我们首先提供了一个构造方法create(),可以提供默认的初始化过程//规则执行器publicclassRuleService{privateMap>hashMap=newHashMap<>();privatestaticfinalintNOT=2;privatestaticfinalintAND=1;privatestaticfinalintOR=0;privateRuleDtoruleDto;publicstaticRuleServicecreate(RuleDtoruleDto){RuleServiceruleService=newRuleService();ruleService.ruleDto=urnDtorule;ret;}publicRuleServiceand(ListruleList){hashMap.put(AND,ruleList);returnthis;}publicRuleServiceor(ListruleList){hashMap.put(OR,ruleList);returnthis;}publicRuleServicenot(ListruleList){hashMap.put(NOT,ruleList);returnthis;}publicbooleanexecute(){returnthis.execute(ruleDto);}privatebooleanexecute(RuleDtodto){for(Map.Entry>item:hashMap.entrySet()){ListruleList=item.getValue();switch(item.getKey()){caseAND://如果是and关系,执行System.out.println("executekey)synchronously="+1);if(!andRule(dto,ruleList)){returnfalse;}break;caseOR://如果是or关系,并行执行System.out.println("executekey="+0);if(!orRule(dto,ruleList)){returnfalse;}break;caseNOT://如果不是关系System.out.println("executekey="+2);if(!notRule(dto,ruleList)){returnfalse;}default:break;}}returntrue;}privatebooleanandRule(RuleDtodto,ListruleList){for(BaseRulerule:ruleList){booleanexecute=rule.execute(dto);if(!execute){//关系匹配失败一次,返回falsereturnfalse;}}//关系全部匹配成功,返回truereturntrue;}privatebooleanorRule(RuleDtodto,ListruleList){for(BaseRulerule:ruleList){booleanexecute=rule.execute(dto);if(execute){//或者关系匹配一个,返回truereturntrue;}}//或者关系不匹配一个,返回falsereturnfalse;}privatebooleannotRule(RuleDtodto,ListruleList){//notruleinternallyisandlinkreturn!andRule(dto,ruleList);}}//规则工厂类publicclassRuleServices{/***学生规则教学与研究**@return*/publicstaticRuleServiceisValidStudent(RuleDtoruleDto){AgeRuleageRule=newAgeRule();NameRulenameRule=newNameRule();NationalityRulenationalityRule=newNationalityRule();AddressRuleaddressRule=newAddressRule();SubjectRulesubjectRule=newSubjectRule();Flag110Ruleflag110Rule=newFlag110Rule();returnRuleService.create(ruleDto).and(ArraysRule.asList,nameRule,addressRule)).or(Arrays.asList(ageRule,subjectRule)).not(Collections.singletonList(flag110Rule));}}客户端调用代码客户端调用主要分为三步:首先,需要构建业务数据,因为规则策略是基于数据处理,从规则工厂获取规则列表后返回规则定义执行器。最后执行规则并返回结果。publicclassRuleServiceTest{@org.junit.Testpublicvoidexecute(){//规则执行器//优点:比较简单,每条规则可以独立,独立的规则、数据、执行器,调用者比较正规//缺点:数据依赖publictransfer对象dto//1。构造所需数据createdtoRuleDtodto=newRuleDto();dto.setAge(5);dto.setName("张三");dto.setAddress("北京");dto.setSubject("数学");;//2.定义规则initruleRuleServiceruleService=RuleServices.isValidStudent(dto);//3.规则执行器ruleexecutebooleanruleResult=ruleService.execute();System.out.println("thisstudentruleexecuteresult:"+ruleResult);}}总结规则执行器优缺点优点:比较简单,每条规则可以独立,规则,数据,和executors分离,调用者比较正规;我在Rule模板类中定义了convert方法来转换参数,这样它就可以为特定规则所需的场景数据提供扩展。缺点:上下游规则有数据依赖。如果直接修改dto传输对象的值不是特别合理,建议使用中间数据来存放临时数据。参考https://www.codenong.com/30430818https://cloud.tencent.com/developer/article/1528935