解释器模式 解释器模式是一种按照规定语法进行解析的方案。
定义 给一门语言,定义它的文法的一种表示,并且定义一个解释器,该解释器使用该表示来解释语言中的句子。
比如Java语言,定义文法的表示是Java的语法。 javac编译器看作是解释器,解释器用来将Java“解释为” .class 格式的文件。
实现 AbstractExpression 抽象解释器,抽象解释器通常只有一个方法, 是生成语法集合的关键,每个语法集合完成指定语法的解析任务,通过递归调用的方式,最终由最小的语法但愿进行解析完成。 具体的解释任务由各个实现类完成。具体的解释器分别由TerminalExpression和Non-terminalExpression完成。
1 2 3 4 5 6 7 8 9 10 11 public abstract class Expression { public abstract int interpreter (HashMap<String,Integer> var ) ; }
TerminalExpression 终结符表达式 实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符的表达式,但是有多个实例,对应不同的终结符。表达式中每个终结符都在栈中产生了一个varExpression。
1 2 3 4 5 6 7 8 9 10 11 12 13 public class VarExpression extends Expression { private String key; public VarExpression (String key) { this .key = key; } @Override public int interpreter (HashMap<String, Integer> var ) { return var .get(this .key); } }
NoneTerminalExpression 非终结符表达式 文法中每条规则都对应一个非终结表达式,具体到我们的例子就是加减法规则分别对应到AddExpression和SubExpression两个类,非终结表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public abstract class SymbolExpression extends Expression { protected Expression left; protected Expression right; public SymbolExpression (Expression left, Expression right) { this .left = left; this .right = right; } } public class AddExpression extends SymbolExpression { public AddExpression (Expression left, Expression right) { super (left, right); } @Override public int interpreter (HashMap<String, Integer> var ) { return super .left.interpreter(var ) + super .right.interpreter(var ); } } public class SubExpression extends SymbolExpression { public SubExpression (Expression left, Expression right) { super (left, right); } @Override public int interpreter (HashMap<String, Integer> var ) { return super .left.interpreter(var ) - super .right.interpreter(var ); } }
Context 环境角色,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class Calculator { private Expression expression; public Calculator (String expStr) { Stack<Expression> stack = new Stack<>(); char [] charArray = expStr.toCharArray(); Expression left = null ; Expression right = null ; for (int i = 0 ; i < charArray.length; i++) { switch (charArray[i]) { case '+' : left = stack.pop(); right = new VarExpression(String.valueOf(charArray[++i])); stack.push(new AddExpression(left, right)); break ; case '-' : left = stack.pop(); right = new VarExpression(String.valueOf(charArray[++i])); stack.push(new SubExpression(left, right)); break ; default : stack.push(new VarExpression(String.valueOf(charArray[i]))); break ; } } this .expression = stack.pop(); } public int run (HashMap<String, Integer> var ) { return this .expression.interpreter(var ); } }
Use 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static void main (String[] args) throws IOException { String expStr = getExpStr(); HashMap<String, Integer> var = getValue(expStr); Calculator calculator = new Calculator(expStr); System.out.println(calculator.run(var )); } private static HashMap<String, Integer> getValue (String expStr) throws IOException { HashMap<String, Integer> map = new HashMap<>(); for (char c : expStr.toCharArray()) { if (c != '+' & c != '-' ) { if (!map.containsKey(String.valueOf(c))) { String in = (new BufferedReader(new InputStreamReader(System.in))).readLine(); map.put(String.valueOf(c), Integer.valueOf(in)); } } } return map; } private static String getExpStr () throws IOException { System.out.println("Input Expression" ); return new BufferedReader(new InputStreamReader(System.in)).readLine(); }
应用 优点 扩展性良好,修改语法规则只需要修改相应的非终结符表达式就可以了。 如果扩展语法,则只要增加非终结符类就可以了。
缺点 解释器模式会引起类膨胀,每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可以能产生大量的类文件。 解释器模式采用递归调用方式,每个表达式需要知道最终的结果,必须一层层递归,无论是面向对象还是面向过程,递归都是在必要条件下才使用,导致调试复杂。
效率问题,解释器模式使用大量的循环和递归,效率时一个很大的问题。
使用场景 重复发生的问题可以解释器模式,多个应用服务器,每天产生大量的日志,需要对日志文件进行分析处理,由于各个服务器的日志格式不同,但是数据要素相同,按照解释器模式终结符表达式相同,非终结符表达式需要定制。
一个简单语法需要解释的场景,解释器模式一般用来解析表标准的字符集,如SQL语法分析。
注意事项 尽量不要在重要的模块中使用解释器模式,维护成本太高。尽量使用shell、JRuby、Groovy来替代。
扩展 成熟的工具 Expression4J MESP: Math Expression String Parser、 Jep.