解释器的英文翻译就是解释器为某个语言定义它的语法表示,并且定义解释器来处理这个语法
这里所说的语言,并不是平时所说的中 英 日 法等语言,而是指的任何能够承载信息的载体
我们根据语言定义对应的语法规则,让书写者根据语法规则来书写句子,阅读者根据语法规则解析这个句子
我们举一个比较贴近现实的例子,我们需要开发一个类似Google Translate的翻译器,根据语法规则,将输入的中文变为英文,这就是对应的中转英的解释器
对应一个编程,就是假设我们需要编写一个计算的解释器
能够对加减乘除进行计算,这里的加减乘除没有优先级的概念
表达式中,先去写数字,再去写运算符
按照先后的顺序,取出了对应的数字和运算符计算结果,重新放入到数字的头部,依次循环,直到只有一个数字,进行输出
假设一个 8 3 2 5 – + *的表达式,按照上面的解析就是 先 取出 8 3 和 – 计算得到了5,变成了 5 2 4 + * ,然后取出 5 2 +,就变成了 7 ,表达式变为了 7 5 * 最后得到了35
对于的代码实现如下
public class ExpressionInterpreter {
private Deque<Long> numbers = new LinkedList<>(); public long interpret(String expression) { String[] elements = expression.split(” “); int length = elements.length; for (int i = 0; i < (length+1)/2; ++i) { numbers.addLast(Long.parseLong(elements[i])); } for (int i = (length+1)/2; i < length; ++i) { String operator = elements[i]; boolean isValid = “+”.equals(operator) || “-“.equals(operator) || “*”.equals(operator) || “/”.equals(operator); if (!isValid) { throw new RuntimeException(“Expression is invalid: ” + expression); } long number1 = numbers.pollFirst(); long number2 = numbers.pollFirst(); long result = 0; if (operator.equals(“+”)) { result = number1 + number2; } else if (operator.equals(“-“)) { result = number1 – number2; } else if (operator.equals(“*”)) { result = number1 * number2; } else if (operator.equals(“/”)) { result = number1 / number2; } numbers.addFirst(result); } if (numbers.size() != 1) { throw new RuntimeException(“Expression is invalid: ” + expression); } return numbers.pop(); } } |
对于上面的代码实现,我们可以简单的放在一个函数中实现,但是如果比较复杂的逻辑的话,就需要考虑进行拆分了,拆分为更小的类,可以将所有的解析分开放,拆分为NumberExpression,AdditionExpression,SubstractionExpression,MultiplicationExpression,DivisionExpression五个解析类中
我们对其进行了重构,
public interface Expression {
long interpret(); } public class NumberExpression implements Expression { private long number; public NumberExpression(long number) { this.number = number; } public NumberExpression(String number) { this.number = Long.parseLong(number); } @Override public long interpret() { return this.number; } } public class AdditionExpression implements Expression { private Expression exp1; private Expression exp2; public AdditionExpression(Expression exp1, Expression exp2) { this.exp1 = exp1; this.exp2 = exp2; } @Override public long interpret() { return exp1.interpret() + exp2.interpret(); } } // SubstractionExpression/MultiplicationExpression/DivisionExpression与AdditionExpression代码结构类似,这里就省略了 public class ExpressionInterpreter { private Deque<Expression> numbers = new LinkedList<>(); public long interpret(String expression) { String[] elements = expression.split(” “); int length = elements.length; for (int i = 0; i < (length+1)/2; ++i) { numbers.addLast(new NumberExpression(elements[i])); } for (int i = (length+1)/2; i < length; ++i) { String operator = elements[i]; boolean isValid = “+”.equals(operator) || “-“.equals(operator) || “*”.equals(operator) || “/”.equals(operator); if (!isValid) { throw new RuntimeException(“Expression is invalid: ” + expression); } Expression exp1 = numbers.pollFirst(); Expression exp2 = numbers.pollFirst(); Expression combinedExp = null; if (operator.equals(“+”)) { combinedExp = new AdditionExpression(exp1, exp2); } else if (operator.equals(“-“)) { combinedExp = new AdditionExpression(exp1, exp2); } else if (operator.equals(“*”)) { combinedExp = new AdditionExpression(exp1, exp2); } else if (operator.equals(“/”)) { combinedExp = new AdditionExpression(exp1, exp2); } long result = combinedExp.interpret(); numbers.addFirst(new NumberExpression(result)); } if (numbers.size() != 1) { throw new RuntimeException(“Expression is invalid: ” + expression); } return numbers.pop().interpret(); } } |
更贴近实际开发的例子的话,可以看我们今天的问题,如何实现一个自定义的接口的告警规则,
就是对于某个接口调用次数超过一定次数,或者出错超过一定次数,然后出发报警,走报警对应的处理流程
假如下面这个表达式
api_error_per_minute > 100 || api_count_per_minute > 10000 |
如果超过了100次的出错,或者调用超过了10000次,就会出发报警
任意元素通过空格来分离,而且用户可以自己定义要监控的key,比如之前的api_error_per_minute和api_count_per_minute
简单的实现如下
public interface Expression {
boolean interpret(Map<String, Long> stats); } public class GreaterExpression implements Expression { private String key; private long value; public GreaterExpression(String strExpression) { String[] elements = strExpression.trim().split(“\\s+”); if (elements.length != 3 || !elements[1].trim().equals(“>”)) { throw new RuntimeException(“Expression is invalid: ” + strExpression); } this.key = elements[0].trim(); this.value = Long.parseLong(elements[2].trim()); } public GreaterExpression(String key, long value) { this.key = key; this.value = value; } @Override public boolean interpret(Map<String, Long> stats) { if (!stats.containsKey(key)) { return false; } long statValue = stats.get(key); return statValue > value; } } // LessExpression/EqualExpression跟GreaterExpression代码类似,这里就省略了 public class AndExpression implements Expression { private List<Expression> expressions = new ArrayList<>(); public AndExpression(String strAndExpression) { String[] strExpressions = strAndExpression.split(“&&”); for (String strExpr : strExpressions) { if (strExpr.contains(“>”)) { expressions.add(new GreaterExpression(strExpr)); } else if (strExpr.contains(“<“)) { expressions.add(new LessExpression(strExpr)); } else if (strExpr.contains(“==”)) { expressions.add(new EqualExpression(strExpr)); } else { throw new RuntimeException(“Expression is invalid: ” + strAndExpression); } } } public AndExpression(List<Expression> expressions) { this.expressions.addAll(expressions); } @Override public boolean interpret(Map<String, Long> stats) { for (Expression expr : expressions) { if (!expr.interpret(stats)) { return false; } } return true; } } public class OrExpression implements Expression { private List<Expression> expressions = new ArrayList<>(); public OrExpression(String strOrExpression) { String[] andExpressions = strOrExpression.split(“\\|\\|”); for (String andExpr : andExpressions) { expressions.add(new AndExpression(andExpr)); } } public OrExpression(List<Expression> expressions) { this.expressions.addAll(expressions); } @Override public boolean interpret(Map<String, Long> stats) { for (Expression expr : expressions) { if (expr.interpret(stats)) { return true; } } return false; } } public class AlertRuleInterpreter { private Expression expression; public AlertRuleInterpreter(String ruleExpression) { this.expression = new OrExpression(ruleExpression); } public boolean interpret(Map<String, Long> stats) { return expression.interpret(stats); } } |
本章重点
这就是简单的解释器模式的解释,定义某种语言,然后定义一个解释器来处理这个语法,实际上这里的语言指的是一种广泛的,能够承载信息的载体,要想要了解语言,就需要定义对应的语法规则,根据语法规则来书写句子,做到正确的信息传递,就是解释器模式
而且这种解释器模式,其实也是讲语法解析的工作进行拆分,拆分到更小的类中,避免大而全的类
课后思考
1.在之前,有没有见到过解释器模式呢?
2.如果在告警规则中,需要增加括号,对代码进行重构,如何办呢?
1.对于一个Java程序员来说,应该知道从Java语言解释为JVM规范语言,是需要进行解释器解析的,从词法解析器,解析出对应的类定义属性等,到语法解析器,解析成对应的语法树,再使用语义解析器,进行判断规范和解析语法糖
2.