首页 > 代码库 > 简明解释器模式(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)