解释器的英文翻译就是解释器为某个语言定义它的语法表示,并且定义解释器来处理这个语法

这里所说的语言,并不是平时所说的中 英 日 法等语言,而是指的任何能够承载信息的载体

我们根据语言定义对应的语法规则,让书写者根据语法规则来书写句子,阅读者根据语法规则解析这个句子

我们举一个比较贴近现实的例子,我们需要开发一个类似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.

发表评论

邮箱地址不会被公开。 必填项已用*标注