聊聊设计模式-解释器模式?
简介
解释器模式属于行为型模式。它是指给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。是一种按照规定的语法进行解析的模式
编译器可以将源码编译解释为机器码,让CPU能进行识别并运行。解释器模式的作用与编译器一样,都是将一些固定的语法进行解释,构建出一个解释句子的解释器
简单理解,解释器是一个简单语法分析工具,它可以识别句子语义,分离终结符号和非终结符号,提取出需要的信息,让我们能针对不同的信息做出相应的处理。其核心思想是识别文法,构建解释
简而言之,解释器模式就是对于一些固定文法构建一个解释句子的解释器
应用场景
- 一些重复出现的问题可以用一种简单的语言来进行表达;
- 一个简单语法需要解释的场景;
主要角色
解释器模式主要包含四种角色
- 抽象表达式(Expression)
负责定义一个解释方法interpret,交由具体子类进行具体解释
- 终结符表达式(TerminalExpression)
实现文法中与终结符有关的解释操作。文法中的每一个终结符都有一个具体终结表达式与之相对应
如公式R = R1 + R2,R1和R2就是终结符,对应的解析R1和R2的解释器就是终结表达式
通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符(R1, R2)
- 非终结符表达式(NonterminalExpression)
实现文法中与非终结符有关的解释操作
文法中的每条规则都对应于一个非终结符表达式
非终结符表达式一般是文法中的运算符或者其他关键字,如公式R=R1+R2中,“+”就是非终结符,解析"+"的解释器就是一个非终结符表达式
非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式
- 上下文环境类(Context)
包含解释器之外的全局信息。它的任务一般是用来存放文法中各个终结符所对应的具体值,比如R=R1+R2,给R1赋值100,给R2赋值200,这些信息需要存放到环境中
优缺点
优点:
- 扩展性强:在解释器模式中由于语法是由很多类表示的,当语法规则更改时,只需修改相应的非终结符表达式即可;若扩展语法时,只需添加相应非终结符类即可;
- 增加了新的解释表达式的方式;
- 易于实现文法:解释器模式对应的文法应当是比较简单且易于实现的,过于复杂的语法并不适合使用解释器模式。
缺点:
- 语法规则较复杂时,会引起类膨胀:解释器模式每个语法都要产生一个非终结符表达式当语法规则比较复杂时,就会产生大量的解释类,增加系统维护困难;
- 执行效率比较低:解释器模式采用递归调用方法,每个非终结符表达式只关心与自关的表达式,每个表达式需要知道最终的结果,因此完整表达式的最终结果是通过从后往前递归调用的方式获取得到;当完整表达式层级较深时,解释效率下降,且出错时调试困难,因为递归迭代层级太深
基本使用
使用解释器模式解析数学表达式
1. 创建抽象表达式
创建一个抽象表达式接口,定义一个表达式解释方法,由具体子类进行具体解释
public interface IExpression {
// 对表达式进行解释
int interpret();
}
2. 创建终结符表达式
public abstract class TerminalExpression implements IExpression {
protected IExpression a;
protected IExpression b;
public TerminalExpression(IExpression a, IExpression b) {
this.a = a;
this.b = b;
}
}
3. 创建非终结符表达式
加操作
public class AddNonterminalExpression extends TerminalExpression {
public AddNonterminalExpression(IExpression a, IExpression b) {
super(a, b);
}
@Override
public int interpret() {
return this.a.interpret() + this.b.interpret();
}
}
减操作
public class SubNonterminalExpression extends TerminalExpression {
public SubNonterminalExpression(IExpression a, IExpression b) {
super(a, b);
}
@Override
public int interpret() {
return this.a.interpret() - this.b.interpret();
}
}
数字操作
public class NumNonterminalExpression implements IExpression {
private int value;
public NumNonterminalExpression(int value) {
this.value = value;
}
@Override
public int interpret() {
return this.value;
}
}
4. 创建上下文环境类
public class CalculatorContext {
private final Stack<IExpression> stack = new Stack<>();
public CalculatorContext(String expression) {
this.parse(expression);
}
// 解释器表达式
private void parse(String expression) {
String[] elements = expression.split(" ");
IExpression aExpr, bExpr;
for (int i = 0, length = elements.length; i < length; i++) {
String operator = elements[i];
if (CalculatorContext.isOperator(operator)) {
aExpr = this.stack.pop();
System.out.println("出栈: " + aExpr.interpret());
bExpr = new NumNonterminalExpression(Integer.parseInt(elements[++i]));
TerminalExpression res = CalculatorContext.util(aExpr, bExpr, operator);
this.stack.push(res);
System.out.println("计算: " + aExpr.interpret() + operator + bExpr.interpret());
System.out.println("计算结果: " + Objects.requireNonNull(res).interpret() + "入栈");
} else {
NumNonterminalExpression numNonterminalExpression = new NumNonterminalExpression(Integer.parseInt(elements[i]));
this.stack.push(numNonterminalExpression);
System.out.println("入栈: " + numNonterminalExpression.interpret());
}
}
}
// 计算结果
public int calculate() {
int interpret = this.stack.pop().interpret();
System.out.println("计算结果:" + interpret + "出栈");
return interpret;
}
public static TerminalExpression util(IExpression a, IExpression b, String symbol) {
if ("+".equals(symbol)) {
return new AddNonterminalExpression(a, b);
} else if ("-".equals(symbol)) {
return new SubNonterminalExpression(a, b);
} else {
return null;
}
}
public static boolean isOperator(String symbol) {
return ("+".equals(symbol) || "-".equals(symbol));
}
}
5. 创建客户端
public class Client {
public static void main(String[] args) {
// 创建上下文对象进行解释
CalculatorContext calculatorContext = new CalculatorContext("10 + 20");
// 获取执行结果
System.out.println("calculatorContext.calculate() = " + calculatorContext.calculate());
CalculatorContext calculatorContext2 = new CalculatorContext("10 + 20 - 15");
System.out.println("calculatorContext.calculate() = " + calculatorContext2.calculate());
}
}
运行结果: