当前位置:网站首页>还在写大量 if 来判断?一个规则执行器干掉项目中所有的 if 判断...
还在写大量 if 来判断?一个规则执行器干掉项目中所有的 if 判断...
2022-07-28 22:25:00 【肥肥技术宅】
业务场景
近日在公司领到一个小需求,需要对之前已有的试用用户申请规则进行拓展。我们的场景大概如下所示:
if (是否海外用户) {
return false;
}
if (刷单用户) {
return false;
}
if (未付费用户 && 不再服务时段) {
return false
}
if (转介绍用户 || 付费用户 || 内推用户) {
return true;
}
按照上述的条件我们可以得出的结论是:
咱们的的主要流程主要是基于 and 或者 or 的关系。
如果有一个不匹配的话,其实咱们后续的流程是不用执行的,就是需要具备一个短路的功能。
对于目前的现状来说,我如果在原有的基础上来改,只要稍微注意一下解决需求不是很大的问题,但是说后面可维护性非常差。
后面进过权衡过后,我还是决定将这个部分进行重构一下。
规则执行器
针对这个需求,我首先梳理了一下咱们规则执行器大概的设计, 然后我设计了一个 V1 版本和大家一起分享一下,如果大家也有这样的 case 可以给我分享留言,下面部分主要是设计和实现的流程和 code.
规则执行器的设计

对于规则的抽象并实现规则
// 业务数据
@Data
public class RuleDto {
private String address;
private int age;
}
// 规则抽象
public interface BaseRule {
boolean execute(RuleDto dto);
}
// 规则模板
public abstract class AbstractRule implements BaseRule {
protected <T> T convert(RuleDto dto) {
return (T) dto;
}
@Override
public boolean execute(RuleDto dto) {
return executeRule(convert(dto));
}
protected <T> boolean executeRule(T t) {
return true;
}
}
// 具体规则- 例子1
public class AddressRule extends AbstractRule {
@Override
public boolean execute(RuleDto dto) {
System.out.println("AddressRule invoke!");
if (dto.getAddress().startsWith(MATCH_ADDRESS_START)) {
return true;
}
return false;
}
}
// 具体规则- 例子2
public class NationalityRule extends AbstractRule {
@Override
protected <T> T convert(RuleDto dto) {
NationalityRuleDto nationalityRuleDto = new NationalityRuleDto();
if (dto.getAddress().startsWith(MATCH_ADDRESS_START)) {
nationalityRuleDto.setNationality(MATCH_NATIONALITY_START);
}
return (T) nationalityRuleDto;
}
@Override
protected <T> boolean executeRule(T t) {
System.out.println("NationalityRule invoke!");
NationalityRuleDto nationalityRuleDto = (NationalityRuleDto) t;
if (nationalityRuleDto.getNationality().startsWith(MATCH_NATIONALITY_START)) {
return true;
}
return false;
}
}
// 常量定义
public class RuleConstant {
public static final String MATCH_ADDRESS_START= "北京";
public static final String MATCH_NATIONALITY_START= "中国";
}
执行器构建
public class RuleService {
private Map<Integer, List<BaseRule>> hashMap = new HashMap<>();
private static final int AND = 1;
private static final int OR = 0;
public static RuleService create() {
return new RuleService();
}
public RuleService and(List<BaseRule> ruleList) {
hashMap.put(AND, ruleList);
return this;
}
public RuleService or(List<BaseRule> ruleList) {
hashMap.put(OR, ruleList);
return this;
}
public boolean execute(RuleDto dto) {
for (Map.Entry<Integer, List<BaseRule>> item : hashMap.entrySet()) {
List<BaseRule> ruleList = item.getValue();
switch (item.getKey()) {
case AND:
// 如果是 and 关系,同步执行
System.out.println("execute key = " + 1);
if (!and(dto, ruleList)) {
return false;
}
break;
case OR:
// 如果是 or 关系,并行执行
System.out.println("execute key = " + 0);
if (!or(dto, ruleList)) {
return false;
}
break;
default:
break;
}
}
return true;
}
private boolean and(RuleDto dto, List<BaseRule> ruleList) {
for (BaseRule rule : ruleList) {
boolean execute = rule.execute(dto);
if (!execute) {
// and 关系匹配失败一次,返回 false
return false;
}
}
// and 关系全部匹配成功,返回 true
return true;
}
private boolean or(RuleDto dto, List<BaseRule> ruleList) {
for (BaseRule rule : ruleList) {
boolean execute = rule.execute(dto);
if (execute) {
// or 关系匹配到一个就返回 true
return true;
}
}
// or 关系一个都匹配不到就返回 false
return false;
}
}
执行器的调用
public class RuleServiceTest {
@org.junit.Test
public void execute() {
//规则执行器
//优点:比较简单,每个规则可以独立,将规则,数据,执行器拆分出来,调用方比较规整
//缺点:数据依赖公共传输对象 dto
//1. 定义规则 init rule
AgeRule ageRule = new AgeRule();
NameRule nameRule = new NameRule();
NationalityRule nationalityRule = new NationalityRule();
AddressRule addressRule = new AddressRule();
SubjectRule subjectRule = new SubjectRule();
//2. 构造需要的数据 create dto
RuleDto dto = new RuleDto();
dto.setAge(5);
dto.setName("张三");
dto.setAddress("北京");
dto.setSubject("数学");;
//3. 通过以链式调用构建和执行 rule execute
boolean ruleResult = RuleService
.create()
.and(Arrays.asList(nationalityRule, nameRule, addressRule))
.or(Arrays.asList(ageRule, subjectRule))
.execute(dto);
System.out.println("this student rule execute result :" + ruleResult);
}
}
总结
规则执行器的优点和缺点
优点:
比较简单,每个规则可以独立,将规则,数据,执行器拆分出来,调用方比较规整;
我在 Rule 模板类中定义 convert 方法做参数的转换这样可以能够,为特定 rule 需要的场景数据提供拓展。
缺点:
上下 rule 有数据依赖性,如果直接修改公共传输对象 dto 这样设计不是很合理,建议提前构建数据。
边栏推荐
- 动态规划问题(六)
- Build SSM project with JSP as view parser
- Web系统常见安全漏洞介绍及解决方案-CSRF攻击
- curl (7) Failed connect to localhost8080; Connection refused
- Virtual lab basic experiment tutorial -8. Fourier transform (1)
- Geth installation
- Add build dependency error
- How NAT configures address translation
- Real time data warehouse: Didi's real-time data warehouse landing practice
- Feign call fails. JSON parse error illegal character ((ctrl-char, code 31)) only regular white space (R
猜你喜欢

Leetcode61. rotating linked list

How NAT configures address translation

Visual full link log tracking
![[CNN] Why is the convolution kernel size of CNN usually odd](/img/d8/a367c26b51d9dbaf53bf4fe2a13917.png)
[CNN] Why is the convolution kernel size of CNN usually odd

Attack and defense world web master advanced area php2

Web系统常见安全漏洞介绍及解决方案-sql注入

PHP poster QR code synthesis

Laravel permission control

Concurrency in go

"Method not allowed", 405 problem analysis and solution
随机推荐
feign调用不通问题,JSON parse error Illegal character ((CTRL-CHAR, code 31)) only regular white space (r
Develop effective Tao spell
Oracle super full SQL, details crazy
PHP poster QR code synthesis
CV target detection model sketch (2)
Interpretation of ISO 13400 (doip) standard
动态规划问题(一)
flyway的快速入门教程
Laravel permission control
@Transactional 注解使用详解
How can Plato obtain premium income through elephant swap in a bear market?
Eye of depth (18) -- partial derivative
JS advanced ES6 ~ es13 new features
Leetcode 763. partition labels divide alphabetic intervals (medium)
JS four formulas for judging data types
Attack and defense world web master advanced area php2
Advanced area of attack and defense world web masters unserialize3
Do like and in indexes in MySQL go
Oracle超全SQL,细节狂魔
Laptop external display