基于多谓词的规则引擎设计优化:谓词链改进咨询
优化Rule Engine谓词链式调用的可行方案
针对当前Rule Engine实现中Predicate链式调用冗余、依赖注入不规范的问题,以下是几个落地性强的优化方案:
1. 重构RuleEngine,支持批量规则组合
原RuleEngine仅支持设置单个Predicate且会覆盖原有规则,先重构为支持批量添加规则并自动组合:
public class RuleEngine { private final List<Predicate<Transaction>> rules = new ArrayList<>(); private Transaction transaction; public void setTransaction(Transaction transaction) { this.transaction = transaction; } // 添加单个规则 public void addRule(Predicate<Transaction> rule) { this.rules.add(rule); } // 批量添加规则 public void addRules(Collection<Predicate<Transaction>> rules) { this.rules.addAll(rules); } public boolean executeRules() { // 自动组合所有规则为逻辑与,空规则集默认返回true Predicate<Transaction> combinedRule = rules.stream() .reduce(Predicate::and) .orElse(t -> true); boolean isValid = combinedRule.test(transaction); if (isValid) { // 交易验证通过逻辑 } return isValid; } }
2. 用规则组封装交易类型对应的规则集合
将不同交易类型的规则列表封装到配置类中,替代硬编码的链式调用,提升可读性和可维护性:
@Component public class PaymentRuleConfig { private final PredicateHelper predicateHelper; // 构造注入替代@Autowired,符合Spring规范 public PaymentRuleConfig(PredicateHelper predicateHelper) { this.predicateHelper = predicateHelper; } public List<Predicate<Transaction>> getCardPaymentRules() { return List.of( predicateHelper.rule1, predicateHelper.rule2, predicateHelper.rule3 ); } public List<Predicate<Transaction>> getCashPaymentRules() { return List.of( predicateHelper.rule1, predicateHelper.rule4, predicateHelper.rule5 ); } }
修改Payments类,直接传入规则列表:
@Component public class Payments { private final PaymentRuleConfig ruleConfig; public Payments(PaymentRuleConfig ruleConfig) { this.ruleConfig = ruleConfig; } public void process(Transaction transaction, String type) { RuleEngine ruleEngine = new RuleEngine(); ruleEngine.setTransaction(transaction); switch (type) { case "card": ruleEngine.addRules(ruleConfig.getCardPaymentRules()); break; case "cash": ruleEngine.addRules(ruleConfig.getCashPaymentRules()); break; default: // 日志记录逻辑 } ruleEngine.executeRules(); } }
3. 用枚举消除Switch-Case,实现类型与规则的映射
进一步用枚举管理交易类型与规则组的对应关系,彻底消除冗余的switch-case:
public enum PaymentType { CARD("card", List.of(predicateHelper.rule1, predicateHelper.rule2, predicateHelper.rule3)), CASH("cash", List.of(predicateHelper.rule1, predicateHelper.rule4, predicateHelper.rule5)); private final String code; private final List<Predicate<Transaction>> rules; PaymentType(String code, List<Predicate<Transaction>> rules) { this.code = code; this.rules = rules; } public static Optional<PaymentType> fromCode(String code) { return Arrays.stream(values()) .filter(t -> t.code.equals(code)) .findFirst(); } public List<Predicate<Transaction>> getRules() { return rules; } }
修改Payments类:
@Component public class Payments { public void process(Transaction transaction, String type) { RuleEngine ruleEngine = new RuleEngine(); ruleEngine.setTransaction(transaction); PaymentType.fromCode(type) .map(PaymentType::getRules) .ifPresent(ruleEngine::addRules); ruleEngine.executeRules(); } }
4. 将规则封装为独立Bean,实现动态组合(进阶方案)
如果规则数量多、需要动态调整,可将每个规则封装为独立Spring Bean,通过自定义注解标记所属交易类型,实现按需加载:
首先定义自定义注解:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface PaymentRule { String[] value(); // 对应的交易类型,比如{"card", "cash"} }
封装每个规则为独立Bean:
@Component @PaymentRule({"card", "cash"}) public class Rule1 implements Predicate<Transaction> { private final TransactionUtils transactionUtils; public Rule1(TransactionUtils transactionUtils) { this.transactionUtils = transactionUtils; } @Override public boolean test(Transaction transaction) { return "rule1".equals(transactionUtils.getName(transaction)); } } @Component @PaymentRule("card") public class Rule2 implements Predicate<Transaction> { private final TransactionUtils transactionUtils; public Rule2(TransactionUtils transactionUtils) { this.transactionUtils = transactionUtils; } @Override public boolean test(Transaction transaction) { return "rule2".equals(transactionUtils.getName(transaction)); } } // Rule3-Rule5同理实现
在RuleConfig中通过Spring上下文获取对应类型的规则:
@Component public class PaymentRuleConfig implements ApplicationContextAware { private ApplicationContext context; @Override public void setApplicationContext(ApplicationContext context) throws BeansException { this.context = context; } public List<Predicate<Transaction>> getRulesByType(String type) { return context.getBeansWithAnnotation(PaymentRule.class).values().stream() .filter(bean -> { PaymentRule annotation = bean.getClass().getAnnotation(PaymentRule.class); return Arrays.asList(annotation.value()).contains(type); }) .map(Predicate.class::cast) .collect(Collectors.toList()); } }
最后Payments类直接调用:
@Component public class Payments { private final PaymentRuleConfig ruleConfig; public Payments(PaymentRuleConfig ruleConfig) { this.ruleConfig = ruleConfig; } public void process(Transaction transaction, String type) { RuleEngine ruleEngine = new RuleEngine(); ruleEngine.setTransaction(transaction); ruleEngine.addRules(ruleConfig.getRulesByType(type)); ruleEngine.executeRules(); } }
5. 用Builder模式简化Predicate链式组合(可选)
如果仍需要保留链式调用写法,可封装Builder类简化:
public class RulePredicateBuilder { private Predicate<Transaction> current; public static RulePredicateBuilder start() { return new RulePredicateBuilder(); } public RulePredicateBuilder and(Predicate<Transaction> rule) { current = current == null ? rule : current.and(rule); return this; } public Predicate<Transaction> build() { return current == null ? t -> true : current; } }
使用方式:
private Predicate<Transaction> getCardRules(PredicateHelper helper) { return RulePredicateBuilder.start() .and(helper.rule1) .and(helper.rule2) .and(helper.rule3) .build(); }
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

