基本介绍
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。
给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
该模式对于复杂的场景实现起来比较困难,实际应用较少,大家了解即可。
模式结构
Context(环境角色):声明一个所有具体表达式都要实现的抽象接口(或者抽象类),接口中主要是一个interpret() 方法,称为解释操作。具体解释任务由它的各个实现类来完成,具体的解释器分别由终结符解释器 TerminalExpression 和非终结符解释器 NonterminalExpression 完成。
AbstractExpression(抽象解释器):实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。终结符一半是文法中的运算单元,比如有一个简单的公式R=R1+R2,在里面 R1 和 R2 就是终结符,对应的解析 R1 和 R2 的解释器就是终结符表达式。
TerminalExpression(终结符表达式):文法中的每条规则对应于一个非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式 R=R1+R2 中,+ 就是非终结符,解析 + 的解释器就是一个非终结符表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
NoterminalExpression(非终结符表达式):这个角色的任务一般是用来存放文法中各个终结符所对应的具体值,比如 R=R1+R2,我们给 R1 赋值 100,给 R2 赋值 200。这些信息需要存放到环境角色中,很多情况下我们使用 Map 来充当环境角色就足够了。
举例说明
使用解释器模式实现数字的加减法
1、抽象解释器
/**
* 抽象解释器
*/
public abstract class AbstractExpression {
public abstract int interpret(Context context);
}
2、非终结符表达式
/**
* 非终结表达式:加法
*/
public class AddExpression extends AbstractExpression {
private final AbstractExpression left;
private final AbstractExpression right;
public AddExpression(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
return left.interpret(context) + right.interpret(context);
}
}
/**
* 非终结表达式:减法
*/
public class SubExpression extends AbstractExpression {
private final AbstractExpression left;
private final AbstractExpression right;
public SubExpression(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
return left.interpret(context) - right.interpret(context);
}
}
3、终结表达式
/**
* 终结表达式:变量
*/
public class Variable extends AbstractExpression {
private final String key;
public Variable(String key) {
this.key = key;
}
@Override
public int interpret(Context context) {
return context.getValue(key);
}
}
4、环境角色
/**
* 环境上下文
*/
public class Context {
private final Map<String, Integer> valueMap = new HashMap<>();
public void addValue(final String key, final int value) {
valueMap.put(key, value);
}
public int getValue(final String key) {
return valueMap.get(key);
}
public Map<String, Integer> getValueMap() {
return valueMap;
}
}
5、测试类
public class Client {
@Test
public void test(){
Context context = new Context();
context.addValue("a", 6);
context.addValue("b", 9);
context.addValue("c", 1);
Variable a = new Variable("a");
Variable b = new Variable("b");
Variable c = new Variable("c");
AbstractExpression addValue = new AddExpression(a, b);
AbstractExpression subValue = new SubExpression(addValue, c);
System.out.println(context.getValueMap());
System.out.println("a + b - c = " + subValue.interpret(context));
}
}
6、运行结果
{a=6, b=9, c=1}
a+b-c=14
模式分析
优点:
- 可扩展性比较好,灵活
- 增加了新的解释表达式的方式
- 易于实现简单文法
缺点:
- 可利用场景比较少
- 对于复杂的文法比较难维护
- 解释器模式会引起类膨胀
- 解释器模式采用递归调用方法
适用场景:
- 有一个简单的语法规则,比如一个 sql 语句,如果我们需要根据 sql 语句进行 rm 转换,就可以使用解释器模式来对语句进行解释。
- 一些重复发生的问题,比如加减乘除四则运算,但是公式每次都不同,有时是 a+b-cd,有时是 ab+c-d,等等等等个,公式千变万化,但是都是由加减乘除四个非终结符来连接的,这时我们就可以使用解释器模式。