首页 > 代码库 > 简明解释器模式(5.3)
简明解释器模式(5.3)
假设系统的一个文本框中,允许用户输入字符串表达式如"5除 2 乘3模4乘6",要求系统能够按照Java的整数乘除运算规则,计算出表达式结果,如12。
假设系统的一个文本框中,允许用户输入字符串表达式如“list 姓名 年龄 学号 sortby 学号”, 要求系统能够从数据库中提取学生信息并按照学号排序。
当应用程序中频繁使用某种文本形式的句子(不管是用户输入的还是从文件中读取的)时,我们需要为定义字符串表达式的语言设计一个解释器(interpreter)。
在实际的应用程序开发中,编写一个解释器的机会较少。而且编写复杂的解释器需要学习《编译原理》。因而yqj2065认为解释器模式(Interpreter Pattern) 是所有设计模式中最难学习的。如果你大致了解《编译原理》方面的知识,其难度系数从7颗星降到4星。因此下一节的内容难度也就4星。
7.1.1 乘法解释器
介绍解释器模式的例子,通常解释某种简单的表达式语言,如加减法解释器、乘除法解释器或布尔运算解释器。例如用户输入字符串"5除 2 乘3模4乘6",程序可以按照整数int乘除运算规则,计算出表达式结果如12。注:简化起见,先不考虑优先级和括号的使用。另外,程序中实际上处理"5 / 2 * 3 % 4 * 6"字符串。
对于简单的字符串表达式的解释,其实不需要太多词法分析方面的知识。为了集中注意力,我们将表达式语言简化到脑残的程度。
- 乘除法表达式由数字和操作符‘*‘,‘/‘,‘%‘构成。
- 数字是表达式,用操作符连接两个数字是表达式,递归地,用操作符连接两个表达式是表达式。所以,258,2*5,2*5/6都是合法的表达式。
- 简单起见,表达式语言要求数字和操作符之间必须有至少一个空格。因此代码 String[] tokens= statement.split("\\s+");
可以容易地将用户输入的字符串分割成表达式语言的最小单元——终结符(terminal)。
解释器模式(Interpreter Pattern)的核心是解释表达式,接口Expr代表/封装表达式,Expr封装了一个interpret()方法,对本表达式(Expr实例)进行解释(计算) 并返回计算的结果。
数字Digit是表达式,所以Digit是Expr的子类型;用操作符连接两个数字是表达式,以Op表示这种表达式,而而MulOp、ModOP和DivOP分别表示乘、模、除表达式。
例程 7 3封装文法 package intent.interpreter.calculator; public interface Expr{ public int interpret(); } package intent.interpreter.calculator; public abstract class Op implements Expr{ protected Expr left,right;// public Op(Expr left,Expr right) { this.left=left; this.right=right; } } package intent.interpreter.calculator; public class MulOp extends Op{ public MulOp(Expr left,Expr right) { super(left,right); } @Override public int interpret() { return super.left.interpret() * super.right.interpret(); } } package intent.interpreter.calculator; public class Digit implements Expr{ private int value; public Digit(int value) { this.value=http://www.mamicode.com/value;>单纯看Expr与Digit和Op的类图,和组合模式、装饰模式完全一样的结构。区别和内在联系后面讨论。
现在,各个零部件都有了,但是整个"5 / 2 * 3 % 4 * 6"字符串还需要形成一个表达式。一个简洁的实现是使用栈构造语法树。
例程 7 4构造语法树 package intent.interpreter.calculator; import java.util.*; public class Calculator{ private Expr expr; public void build(String statement){ String[] tokens=statement.split(" "); Expr left=null,right=null; Stack<Expr> stack=new Stack<>(); for(int i=0;i<tokens.length;i++){ if(tokens[i].equals("*")){ left= stack.pop(); right=new Digit(tokens[++i]); stack.push(new MulOp(left,right)); } else if(tokens[i].equals("/")){ left= stack.pop(); right=new Digit(tokens[++i]); stack.push(new DivOp(left,right)); }else if(tokens[i].equals("%")){ left= stack.pop(); right=new Digit(tokens[++i]); stack.push(new ModOp(left,right)); }else { stack.push(new Digit(tokens[i])); } } this.expr=(Expr)stack.pop(); } public int compute(){ return expr.interpret(); } } public class Client{ public static void main(String args[]) { String statement = "6 / 2 * 3 % 4 * 6"; statement = "62 / 3"; Calculator calculator = new Calculator(); calculator.build(statement); int result = calculator.compute(); System.out.println(statement + " = " + result); } public static void test() { Expr e = new DivOp(new Digit(62),new Digit(3)); int result = e.interpret(); System.out.println(" 62 / 3 = " + result); } }[设计模式]中的解释器模式并未解释如何创建一个抽象的语法树。
Calculator的方法build(String statement)将参数statement解析并构造一个Expr实例。
也可以直接由Client直接定义Expr实例,如test()代码所示,并执行其interpret()。这样纯手工的玩意,看上去蛮有趣和亲切的——因为傻傻的。Expr e = new DivOp ( new Digit(62),new Digit(3 ) );
简明解释器模式(5.3)