解释器这个名词想必你们都不会陌生,好比编译原理中,一个算术表达式经过词法分析器造成词法单元,然后这些词法单元再经过语法分析器构建语法分析树,最终造成一颗抽象的语法分析树。诸如此类的例子也有不少,好比编译器、正则表达式等等。git
若是一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子,这样就能够构建一个解释器,该解释器经过解释这些句子来解决该问题。正则表达式
就好比正则表达式,它就是解释器模型的一种应用,解释器为正则表达式定义了一个文法,如何表示一个特定的正则表达式,以及如何解释这个正则表达式。express
解释器模式(Interpreter),给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。UML结构图以下:数组
其中,Context是环境角色,包含解释器以外的一些全局信息;AbstractExpression为抽象表达式,声明一个抽象的解释操做,这个接口为抽象语法树中全部的节点所共享;TerminalExression为终结符表达式,实现与文法中的终结符相关联的解释操做;NonterminalExpression为非终结符表达式,为文法中的非终结符实现解释操做,对文法中每一条规则R一、R2……Rn都须要一个具体的非终结符表达式类。ide
1 public class Context { 2 3 private String input; 4 private String output; 5 6 public String getInput() { 7 return input; 8 } 9 public void setInput(String input) { 10 this.input = input; 11 } 12 public String getOutput() { 13 return output; 14 } 15 public void setOutput(String output) { 16 this.output = output; 17 } 18 19 }
抽象表达式是生成语法集合(语法树)的关键,每一个语法集合完成指定语法解析任务,它是经过递归调用的方式,最终由最小的语法单元进行解析完成。函数
1 public abstract class AbstractExpression { 2 public abstract void Interpret(Context context); 3 }
一般,终结符表达式比较简单,主要处理场景元素和数据的转换。this
1 public class TerminalExpression extends AbstractExpression { 2 3 @Override 4 public void Interpret(Context context) { 5 System.out.println("终端解释器"); 6 } 7 8 }
每一个非终结符表达式都表明了一个文法规则,而且每一个文法规则都只关心本身周边的文法规则的结果,所以这就产生了每一个非终结符表达式调用本身周边的非终结符表达式,而后最终、最小的文法规则就是终结符表达式。spa
1 public class NonterminalExpression extends AbstractExpression { 2 3 @Override 4 public void Interpret(Context context) { 5 System.out.println("非终端解释器"); 6 } 7 8 }
其中list为一个语法容器,容纳一个具体的表达式。一般Client是一个封装类,封装的结果就是传递进来一个规范语法文件,解析器分析后产生结果并返回,避免了调用者与语法分析器的耦合关系。调试
1 public class Client { 2 3 public static void main(String[] args) { 4 Context context = new Context(); 5 List<AbstractExpression> list = new ArrayList<>(); 6 7 list.add(new TerminalExpression()); 8 list.add(new NonterminalExpression()); 9 list.add(new TerminalExpression()); 10 list.add(new TerminalExpression()); 11 12 for (AbstractExpression abstractExpression : list) { 13 abstractExpression.Interpret(context); 14 } 15 } 16 17 }
运行结果以下:code
咱们如今经过解释器模式来实现四则运算,如计算a+b的值。UML图以下:
使用Calculator构造函数传参,并解析封装。这里根据栈的“先进后出”来安排运算的前后顺序(主要用在乘除法,这里只写了加减法比较简单)。以加法为例,Calculator构造函数接收一个表达式,而后把表达式转换为char数组,并判断运算符号,若是是‘+’则进行加法运算,把左边的数(left变量)和右边的数(right变量)加起来便可。
例如a+b-c这个表达式,根据for循环,首先被压入栈中的是a元素生成的VarExpression对象,而后判断到加号时,把a元素的对象从栈中pop出来,与右边的数组b进行相加,而b是经过当前的数组游标下移一个单元格得来的(为了防止该元素被再次遍历,经过++i的方式跳过下一遍历)。减法运算同理。
1 public class Calculator { 2 3 //定义表达式 4 private Expression expression; 5 6 //构造函数传参,并解析 7 public Calculator(String expStr) { 8 //安排运算前后顺序 9 Stack<Expression> stack = new Stack<>(); 10 //表达式拆分为字符数组 11 char[] charArray = expStr.toCharArray(); 12 13 Expression left = null; 14 Expression right = null; 15 for(int i=0; i<charArray.length; i++) { 16 switch (charArray[i]) { 17 case '+': //加法 18 left = stack.pop(); 19 right = new VarExpression(String.valueOf(charArray[++i])); 20 stack.push(new AddExpression(left, right)); 21 break; 22 case '-': //减法 23 left = stack.pop(); 24 right = new VarExpression(String.valueOf(charArray[++i])); 25 stack.push(new SubExpression(left, right)); 26 break; 27 default: //公式中的变量 28 stack.push(new VarExpression(String.valueOf(charArray[i]))); 29 break; 30 } 31 } 32 this.expression = stack.pop(); 33 } 34 35 //计算 36 public int run(HashMap<String, Integer> var) { 37 return this.expression.interpreter(var); 38 } 39 40 }
经过Map键值对,使键对应公式参数,如a、b、c等,值为运算时取得的具体数值。
1 public abstract class Expression { 2 3 //解析公式和数值,key是公式中的参数,value是具体的数值 4 public abstract int interpreter(HashMap<String, Integer> var); 5 6 }
经过interpreter()方法从map中取之。
1 public class VarExpression extends Expression { 2 3 private String key; 4 5 public VarExpression(String key) { 6 this.key = key; 7 } 8 9 @Override 10 public int interpreter(HashMap<String, Integer> var) { 11 return var.get(this.key); 12 } 13 14 }
这里,每一个运算符合都只和本身左右两个数字有关系,但左右两个数字有可能也是一个解析的结果,不管何种类型,都是Expression类的实现类。
1 public class SymbolExpression extends Expression { 2 3 protected Expression left; 4 protected Expression right; 5 6 public SymbolExpression(Expression left, Expression right) { 7 this.left = left; 8 this.right = right; 9 } 10 11 @Override 12 public int interpreter(HashMap<String, Integer> var) { 13 // TODO Auto-generated method stub 14 return 0; 15 } 16 17 }
1 public class AddExpression extends SymbolExpression { 2 3 public AddExpression(Expression left, Expression right) { 4 super(left, right); 5 } 6 7 public int interpreter(HashMap<String, Integer> var) { 8 return super.left.interpreter(var) + super.right.interpreter(var); 9 } 10 11 }
1 public class SubExpression extends SymbolExpression { 2 3 public SubExpression(Expression left, Expression right) { 4 super(left, right); 5 } 6 7 public int interpreter(HashMap<String, Integer> var) { 8 return super.left.interpreter(var) - super.right.interpreter(var); 9 } 10 11 }
这里就比较简单了,经过getExpStr()方法获取表达式,再经过getValue()方法获取值的映射,最后再实例化Calculator类,经过run()方法获取最终的运算结果。
1 public class Client { 2 3 public static void main(String[] args) throws IOException { 4 String expStr = getExpStr(); 5 HashMap<String, Integer> var = getValue(expStr); 6 Calculator calculator = new Calculator(expStr); 7 System.out.println("运算结果:" + expStr + "=" + calculator.run(var)); 8 } 9 10 //得到表达式 11 public static String getExpStr() throws IOException { 12 System.out.print("请输入表达式:"); 13 return (new BufferedReader(new InputStreamReader(System.in))).readLine(); 14 } 15 16 //得到值映射 17 public static HashMap<String, Integer> getValue(String expStr) throws IOException { 18 HashMap<String, Integer> map = new HashMap<>(); 19 20 for(char ch : expStr.toCharArray()) { 21 if(ch != '+' && ch != '-' ) { 22 if(! map.containsKey(String.valueOf(ch))) { 23 System.out.print("请输入" + String.valueOf(ch) + "的值:"); 24 String in = (new BufferedReader(new InputStreamReader(System.in))).readLine(); 25 map.put(String.valueOf(ch), Integer.valueOf(in)); 26 } 27 } 28 } 29 30 return map; 31 } 32 33 }
运算结果以下: