规则执行器

19

主要处理的问题:业务中出现多个IF判断,相互之间可能存在AND或OR的关系,为了后期的维护可以采用规则执行器。

if (是否海外用户) {
 return false;
}

if (刷单用户) {
  return false;
}

if (未付费用户 && 不再服务时段) {
  return false
}

if (转介绍用户 || 付费用户 || 内推用户) {
  return true;
}

规则执行器

规则执行器设计:

代码结构:

pojo:

// 数据转换
@Data
public class NationalityRuleDto {
  private String nationality;
}
// 业务数据
@Data
public class RuleDto {
  private String address;
  private int age;
  private String name;
  private String subject;
}

rule:

// 规则抽象
public interface BaseRule {
  boolean execute(RuleDto dto);
}
// 规则模板
public abstract class AbstractRule implements BaseRule {

  // 需要时对传入的Dto进行数据的转换
  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;
  }
}

rule-impl:

// 具体规则- 例子1
public class AddressRule extends AbstractRule {

  @Override
  public boolean execute(RuleDto dto) {
    System.out.println("AddressRule invoke!");
    if (dto.getAddress().startsWith(RuleConstant.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(RuleConstant.MATCH_ADDRESS_START)) {
      nationalityRuleDto.setNationality(RuleConstant.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(RuleConstant.MATCH_NATIONALITY_START)) {
      return true;
    }
    return false;
  }
}
public class AgeRule extends AbstractRule {}
public class NameRule extends AbstractRule {}
public class SubjectRule extends AbstractRule {}

demo:

// 常量定义
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 {

  public static void main(String[] args) {
    // 规则执行器
    // 优点:比较简单,每个规则可以独立,将规则,数据,执行器拆分出来,调用方比较规整
    //      在 Rule 模板类中定义 convert 方法做参数的转换这样可以能够,为特定 rule 需要的场景数据提供拓展
    // 缺点:数据依赖公共传输对象 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);
  }
}