解释器模式
1.解释器模式是什么
1.百度百科
解释器模式(InterpreterPattern)定义一些语法规则,然后定义一个解析器去解析该语法
2.维基百科
In computer programming, the interpreter pattern is a design pattern that specifies how to evaluate sentences in a language. The basic idea is to have a class for each symbol (terminal or nonterminal) in a specialized computer language. The syntax tree of a sentence in the language is an instance of the composite pattern and is used to evaluate (interpret) the sentence for a client.See also Composite pattern.
3.lz理解
相当于定义了一些语法规则,然后定义一个能解析该语法规则的解释器。
4.核心角色
抽象解释器(AbstractExpression) 约定解释器的解释操作。其中的Interpret接口,正如其名字那样,它是专门用来解释该解释器所要实现的功能。(如加法解释器中的Interpret接口就是完成两个操作数的相加功能)。
终结符解释器(TerminalExpression) 用来实现语法规则中和终结符相关的操作,不再包含其他的解释器,如果用组合模式来构建抽象语法树的话,就相当于组合模式中的叶子对象,可以有多种终结符解释器。
非终结符解释器(NonterminalExpression) 用来实现语法规则中非终结符相关的操作,通常一个解释器对应一个语法规则,可以包含其他解释器,如果用组合模式构建抽象语法树的话,就相当于组合模式中的组合对象。可以有多种非终结符解释器。
上下文环境(Context) 通常包含各个解释器需要的数据或是公共的功能。这个Context在解释器模式中起着非常重要的作用。一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
客户端(Client) 指的是使用解释器的客户端,通常在这里将按照语言的语法做的表达式转换成使用解释器对象描述的抽象语法树,然后调用解释操作。
2.解释器模式解决了什么问题
1、可扩展性比较好,灵活。 2、增加了新的解释表达式的方式。 3、易于实现简单文法。
3.解释器模式用法
我们就以加减法的解释器来说明问题
抽象解释器、可以实例化为终结符解释器即操作符,可以实例化为非终结符解释器即变量
//抽象解释器
public interface Expression {
/**
* 以环境为准,本方法解释给定的任何一个表达式
*/
int interpret(Context ctx);
/**
* 将表达式转换成字符串
*/
String toString();
}
终结符解释器
public class Plus implements Expression {
private Expression left, right;
public Plus(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context ctx) {
return left.interpret(ctx) + right.interpret(ctx);
}
@Override
public String toString() {
return "(" + left.toString() + " + " + right.toString() + ")";
}
}
//减法
public class Minus implements Expression {
//减法表达式左边 右边
private Expression left, right;
public Minus(Expression left, Expression right) {
this.left = left;
this.right = right;
}
//从上下文环境中取值并计算
@Override
public int interpret(Context ctx) {
return left.interpret(ctx) - right.interpret(ctx);
}
@Override
public String toString() {
return "(" + left.toString() + " - " + right.toString() + ")";
}
}
终结符解释器、这里重写了 equals和哈希code方法以便于对比两个变量是否相等。
/**
* 常量
* 可以直接使用的常量
*/
public class Constant implements Expression {
private int value;
public Constant(int value) {
this.value = value;
}
@Override
public int interpret(Context ctx) {
return value;
}
@Override
public String toString() {
return new Integer(value).toString();
}
@Override
public boolean equals(Object obj) {
if (obj != null && obj instanceof Constant) {
return this.value == ((Constant) obj).value;
}
return false;
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
}
/**
* 变量
*
* 将变量存储到变量表中
*/
public class Variable implements Expression {
private String name;
public Variable(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
@Override
public int interpret(Context ctx) {
return ctx.lookup(this);
}
@Override
public boolean equals(Object obj) {
if (obj != null && obj instanceof Variable) {
return this.name.equals(((Variable) obj).name);
}
return false;
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
}
上下文环境、一般用于存储变量的值等功能
/**
* 上下文环境 用来存储多次调用的数据
*
* 相当于变量临时存储的表
*/
public class Context {
private Map<Variable, Integer> map = new HashMap<Variable, Integer>();
public void assign(Variable var, int value) {
map.put(var, new Integer(value));
}
public int lookup(Variable var) throws IllegalArgumentException {
Integer value = map.get(var);
if (value == null) {
throw new IllegalArgumentException();
}
return value.intValue();
}
}
客户端调用
public class Client {
public static void main(String[] args) {
//创建一个变量存储表 存储变量
Context ctx = new Context();
//声明各种常量和变量
Variable x = new Variable("x");
Variable y = new Variable("y");
Constant c = new Constant(1);
//将变量赋值
ctx.assign(x, 4);
ctx.assign(y, 3);
//计算公式
Expression e = new Plus(c, x);
System.err.println(x.toString()+" = "+x.interpret(ctx));
//System.err.println(c.toString()+" = "+c.interpret(ctx));
System.err.println(e.toString());
System.err.println("结果是:" + e.interpret(ctx));
Expression exp = new Plus(new Plus(c, x), new Minus(y, x));
System.out.println(exp.toString() + "=" + exp.interpret(ctx));
Expression exp1 = new Plus(new Plus(c, x), c);
System.out.println(exp1.toString() + "=" + exp1.interpret(ctx));
}
}
结果
x = 4
(1 + x)
结果是:5
((1 + x) + (y - x))=4
((1 + x) + 1)=6
可以看出操作符中可以嵌套变量常量。增加新的操作符非常方便
4.解释器模式的问题
使用场景少 可利用场景比较少。
代码调用结构复杂 对于复杂的文法比较难维护。
类膨胀 解释器模式会引起类膨胀。
递归调用 解释器模式采用递归调用方法。
5.解释器模式总结
应用场景:
1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
2、一些重复出现的问题可以用一种简单的语言来进行表达。
3、一个简单语法需要解释的场景。
碎碎念:
想要明白解释器模式首先要明白访问者模式的调用方式和对递归的实现原理有一个清晰的认识。解释器模式可以通过不断叠加的方式去解释一个冗长的语句 并且增加新的功能非常方便完全不影响之前代码的结构。从结构设计上来说可谓是非常精妙了。从变化的角度来说。解释器模式封装了调用结构,开放了具体调用方式和执行方式的拓展,虽然如此但是解释器模式使用场景并不多。基本上能用到解释器模式的前辈已经被我们造好。我们要做的就是愉快的调用就好了。