21-12-06
banq
规则引擎模式:这种模式的本质是if - else ,拆分if - else每个分支到规则类中,然后,主规则引擎类将保存所有规则并找到与客户端请求匹配的规则。
定义规则类
为了确保所有规则类都实现相同的方法,让我们定义一个每个类都将实现的接口:
public interface AncestorRule { Optional<AncestorResult> evaluate(String selector); } |
接下来,让我们定义第一个规则类。该类将保存if - else分支中定义的逻辑:
public class AncestorWithClassRule implements AncestorRule { @Override public Optional<AncestorResult> evaluate(String selector) { if (isCssClass(selector)) { String xpath = format( "ancestor::*[contains(concat(' ', normalize-space(@class), ' '), ' %s ')][%s]", selector.substring(1) ); return Optional.of(new AncestorResult(xpath)); } return Optional.empty(); } } |
isCssClass()判断如果它是一个 CSS 类,则会构建一个 XPath 表达式并将其作为一i个Optional 返回。
这个规则类干净、简短、易于理解。一次编写,不需要经常修改,除非规则的业务逻辑有更新。
我们以同样的方式定义其他规则。验证输入是否与给定条件匹配,构建并返回相应的 XPath 表达式。否则,结果为空。
规则结果
上面的代码使用了AncestorResult. 这个类的目的是包装成功评估的结果。这个类如下所示:
public class AncestorResult { private final String value; public AncestorResult(String value) { this.value = value; } public String getValue() { return value; } } |
只是我们通过构造函数设置的一个类字段并使用 getter 访问它。
规则引擎类
现在,让我们最终进入包含规则引擎逻辑的类。
public class AncestorRuleEngine { private static final List<AncestorRule> rules = Arrays.asList( new AncestorWithTagRule(), new AncestorWithClassRule(), new AncestorWithAttributeRule(), new AncestorWithAttributeAndValueRule() ); public AncestorResult process(String selector) { return rules .stream() .map(rule -> rule.evaluate(selector)) .flatMap(optional -> optional.map(Stream::of).orElseGet(Stream::empty)) .findFirst() .orElseThrow(() -> new IllegalArgumentException("Selector does not match any rule")); } } |
在这个类中做的第一件事是一个适用于这个域的所有规则的静态列表。如果我们有一个新规则——我们实现一个新类并将它添加到这个规则列表中。
第二件事是跨所有规则处理客户端的输入。它流式传输规则列表,评估每个规则。规则的第一个非空结果被返回给客户端。否则,规则引擎将抛出异常。
前一个map()函数返回一个可选的流。然后, flatMap()将空 Optionals 流转换为空流。否则,将一个非空的 AncestorResults 流封装到 Optional 中。该构造与 Java 8 兼容并且看起来很冗长。幸运的是,从 Java 9 开始,这可以简化。
规则引擎的使用
现在,当我们实现了所有或规则时,规则引擎就定义好了,让我们看看如何调用和使用这个引擎。
public class ClientSideThatCallsTheRuleEngine { public void executeClientCode() { // some executions AncestorRuleEngine ruleEngine = new AncestorRuleEngine(); String xpath = ruleEngine.process(selector).getValue(); // other executions } |
它是如此简单。实例化规则引擎。传入客户端的输入,得到结果。它干净、简短且精确。我们隐藏了验证输入、构建相应结果、处理它的所有低级逻辑。将其与具有多个if - else分支的直接方法进行比较。我们添加的逻辑越多,这个if - else怪物就会成长得越多。