设计模式之解释器模式

1 引子

今天介绍设计模式系列的最后一种-解释器设计模式。在了解解释器模式原理后,感觉实际落地的场景非常少,因为它适用于类似API的场景,即定义一种语法规则,同时定义一套基于语法规则的解释器。这里,参考前辈的例子,用计算后缀表达式的值来说明解释器模式。
首先看一下例子的UML图:
设计模式之解释器模式
类图中,接口Expression含有抽象方法interpret(),意为解释执行。其他所有的类实现这个接口,这里称实体类为基本元素,主要定义各种运算规则(加减乘除);Evaluator实体类主要作用是根据运算规则定义解释器,例如遇到“+”字符应该调用Plus对象的interpret()方法,遇到“/”字符应该调用Division对象的interpret()方法等。具体代码如下:

public interface Expression {
	int interpret(Map<String, Expression> variables);
}
// 表达式中各元素
class Number implements Expression {
	private int number;
	public Number(int number){
		this.number = number;
	}
	@Override
	public int interpret(Map<String, Expression> variables) {
		return number;
	}
}

class Plus implements Expression{
	Expression leftOperation;
	Expression rightOperation;
	public Plus(Expression left, Expression right){
		this.leftOperation = left;
		this.rightOperation = right;
	}
	@Override
	public int interpret(Map<String, Expression> variables){
		return leftOperation.interpret(variables) + rightOperation.interpret(variables);
	}
}
class Minus implements Expression{
	Expression leftOperation;
	Expression rightOperation;
	public Minus(Expression left, Expression right){
		this.leftOperation = left;
		this.rightOperation = right;
	}
	@Override
	public int interpret(Map<String, Expression> variables){
		return leftOperation.interpret(variables) - rightOperation.interpret(variables);
	}
}
class Multi implements Expression{
	Expression leftOperation;
	Expression rightOperation;
	public Multi(Expression left, Expression right){
		this.leftOperation = left;
		this.rightOperation = right;
	}
	@Override
	public int interpret(Map<String, Expression> variables){
		return leftOperation.interpret(variables) * rightOperation.interpret(variables);
	}
}
class Division implements Expression{
	Expression leftOperation;
	Expression rightOperation;
	public Division(Expression left, Expression right){
		this.leftOperation = left;
		this.rightOperation = right;
	}
	@Override
	public int interpret(Map<String, Expression> variables){
		if ( rightOperation.interpret(variables) == 0) {
			return 0;
		}
		return leftOperation.interpret(variables) / rightOperation.interpret(variables);
	}
}
class Variable implements Expression{
	private String name;
	public Variable (String name) {
		this.name = name;
	}
	@Override
	public int interpret(Map<String, Expression> variables) {
		if (null == variables.get(name)) {
			return 0;
		}
		return variables.get(name).interpret(variables);
	}
}

解释器:

public class Evaluator implements Expression {
	private Expression syntaxTree;
	public Evaluator (String expression) {
		Stack<Expression> expressionStack = new Stack<Expression>();
		for (String token : expression.split(" ")) {
			if ("+".equals(token)) {
				Expression plusExpression = new Plus(expressionStack.pop(), expressionStack.pop());
				expressionStack.push(plusExpression);
			} else if ("-".equals(token)) {
				Expression minusExpression = new Minus(expressionStack.pop(), expressionStack.pop());
				expressionStack.push(minusExpression);
			} else if ("*".equals(token)) {
				Expression multiExpression = new Multi(expressionStack.pop(), expressionStack.pop());
				expressionStack.push(multiExpression);
			} else if ("/".equals(token)) {
				Expression divisionExpression = new Division(expressionStack.pop(), expressionStack.pop());
				expressionStack.push(divisionExpression);
			} else {
				expressionStack.push(new Variable(token));
			}
		}
		syntaxTree = expressionStack.pop();
	}

	@Override
	public int interpret(Map<String, Expression> variables) {
		return syntaxTree.interpret(variables);
	}
}

客户端调用:

public class Client {
	public static void main(String[] args) {
		String expression = "x y z s - * +";
		Evaluator evaluator = new Evaluator(expression);
		Map<String, Expression> variables = new HashMap<String, Expression>();
		variables.put("x", new Number(7));
		variables.put("y", new Number(2));
		variables.put("z", new Number(6));
		variables.put("s", new Number(4));
		System.out.println(evaluator.interpret(variables));
	}
}

运行结果:3

类似于这种定义规则,并给出基于规则的解释器的代码结构称为解释器模式。

2 解释器模式原理

《大话设计模式》中这样定义解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
这里有两个重点:一是文法,二是解释器。那么,什么是文法?什么是解释器?
文法是用于描述语言的语法结构的形式规则解释器则是根据文法定义的规则解析要解析的内容。我们熟悉的正则表达式是解释器模式的运用;正则表达式的文法就是指定义的各种规则,如“^”,“?”,“x|y”等元字符,正则表达式的解释器则是指正则引擎,解释器能解释一个正则表达式语句;还有,javac也是一种解释器模式,javac能够解析一个.java文件,把文件中的内容作为字符流读取,然后根据java语言的语法规则解析字符流中的Token,并形成抽象语法树(AST)。因此,解释器模式是定义一个新的文法规则,然后用解释器解释表达式的内容。
解释器模式的UML类图:
设计模式之解释器模式
解释器模式中各角色:
AbstractExpression:抽象表达式角色,定义解释器的接口,并约定解释器的解释操作,含有抽象解释方法interpret()。
TerminalExpression:终结符表达式角色,实现解释器接口,用来定义各种文法规则,也即是实例中的运算规则;这里通过Map的形式形成终结符与该终结符对应的表达式之间的映射关系。
NonTerminalExpression:非终结符表达式角色,实现解释器接口,用来处理非终结符的情况,即实例中的解释器Evaluator类。
Context:环境角色,通常包含各个解释器需要的数据或者公共的功能,一般用来传递被所有解释器共享的数据,实例中Context角色与客户端融为一体。
可以看到,解释器模式关键在于终结符表达式角色和非中介符表达式角色,即文法规则和基于规则的解释器。

3 解释器模式特点

解释器模式有哪些优点?
-易于增加文法规则,扩展性好。
-文法规则简单。

解释器模式有哪些缺点?
-多规则文法情况,难以维护。
-使用场景并不多。

4 解释器模式使用场景

《大话设计模式》中指出,当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。在实际业务中,设计模式的复杂性使得极少使用。

5 参考资料

《大话设计模式》
https://www.cnblogs.com/chenpi/p/5222496.html
http://c.biancheng.net/view/1402.html