解释器模式是一种行为设计模式,可以解释语言的语法或表达式。给定一个语言,定义它的文法的一种表示,然后定义一个解释器,使用该文法来解释语言中的句子。解释器模式提供了评估语言的语法或表达式的方式。
Interpreter is a behavior design pattern. It can interpret the syntax or expressions of a language.
Given a language, define a representation of its grammar, and then define an interpreter
that uses the grammar to interpret sentences by the language.
结构设计
解释器模式包含如下角色:
Context,上下文,包含解释器之外的一些全局信息。
AbstractExpression,抽象表达式,声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。
TerminalExression,终结符表达式,实现与文法中的终结符相关联的解释操作。
NonterminalExpression,非终结符表达式,实现与文法中的非终结符相关联的解释操作,对文法中每一条规则R1、R2、…、Rn 都需要一个具体的非终结符表达式类。
Client,客户端,构建一个句子,它是TerminalExression和NonterminalExpression的实例的一个抽象语法树,然后初始化Context,并调用解释操作。
解释器模式类图表示如下:
伪代码实现
接下来将使用代码介绍下解释器模式的实现。由于无法使用抽象的用例表示出解释器模式,所以这里会基于特定的场景给出代码示例。这里以常见的四则运算(由于除法需要特殊处理,这里暂不提供)的解析为例,
介绍下解释器模式的实现。
// 1、抽象表达式,声明一个抽象的解释操作接口
public interface Expression {
int interpret();
}
//2、终结符表达式,实现与文法中的终结符相关联的解释操作,这里是数字
public class NumberExpression implements Expression {
private int number;
public NumberExpression(int number) {
this.number = number;
}
public NumberExpression(String number) {
this.number = Integer.parseInt(number);
}
@Override
public int interpret() {
return this.number;
}
}
// 3、非终结符表达式,实现与文法中的非终结符相关联的解释操作,这里是运算符
public class AdditionExpression implements Expression {
private Expression firstExpression, secondExpression;
public AdditionExpression(Expression firstExpression, Expression secondExpression) {
this.firstExpression = firstExpression;
this.secondExpression = secondExpression;
}
@Override
public int interpret() {
return Math.addExact(this.firstExpression.interpret(), this.secondExpression.interpret());
}
@Override
public String toString() {
return "+";
}
}
public class SubtractionExpression implements Expression {
private Expression firstExpression, secondExpression;
public SubtractionExpression(Expression firstExpression, Expression secondExpression) {
this.firstExpression = firstExpression;
this.secondExpression = secondExpression;
}
@Override
public int interpret() {
return Math.subtractExact(this.firstExpression.interpret(), this.secondExpression.interpret());
}
@Override
public String toString() {
return "-";
}
}
public class MultiplicationExpression implements Expression {
private Expression firstExpression, secondExpression;
public MultiplicationExpression(Expression firstExpression, Expression secondExpression) {
this.firstExpression = firstExpression;
this.secondExpression = secondExpression;
}
@Override
public int interpret() {
return Math.multiplyExact(this.firstExpression.interpret(), this.secondExpression.interpret());
}
@Override
public String toString() {
return "*";
}
}
// 4、表达式分析器,将输入解析成表达式并执行相关的计算
public class ExpressionParser {
private static final String ADD = "+";
private static final String SUBTRACT = "-";
private static final String MULTIPLY = "*";
private static final String SPLITTER = " ";
private LinkedList<Expression> stack = new LinkedList();
public int parse(String str) {
String[] tokenList = str.split(SPLITTER);
for (String symbol : tokenList) {
if (!isOperator(symbol)) {
Expression numberExpression = new NumberExpression(symbol);
stack.push(numberExpression);
} else {
Expression firstExpression = stack.pop();
Expression secondExpression = stack.pop();
Expression operator = getExpressionObject(firstExpression, secondExpression, symbol);
if (operator == null) {
throw new RuntimeException("unknown symbol: " + symbol);
}
int result = operator.interpret();
NumberExpression resultExpression = new NumberExpression(result);
stack.push(resultExpression);
}
}
return stack.pop().interpret();
}
private boolean isOperator(String symbol) {
return symbol.equals(ADD) || symbol.equals(SUBTRACT) || symbol.equals(MULTIPLY);
}
private Expression getExpressionObject(Expression firstExpression, Expression secondExpression, String symbol) {
switch (symbol) {
case ADD:
return new AdditionExpression(firstExpression, secondExpression);
case SUBTRACT:
return new SubtractionExpression(firstExpression, secondExpression);
case MULTIPLY:
return new MultiplicationExpression(firstExpression, secondExpression);
default:
return null;
}
}
}
// 5、客户端
public class InterpreterClient {
public void test() {
// (1) 定义输入
String input = "2 1 5 + *";
System.out.println("input is: " + input);
// (2) 创建表达式分析器实例
ExpressionParser expressionParser = new ExpressionParser();
// (3) 执行分析操作
int result = expressionParser.parse(input);
System.out.println("result: " + result);
}
}
适用场景
在以下情况下可以考虑使用解释器模式:
(1)如果需要解释执行的语言中的句子,可以表示为一个抽象语法树,可以考虑使用解释器模式。如SQL 解析、符号处理引擎、正则表达式等。
(2) 对于重复出现的问题,如果可以使用简单的语言来表达,可以考虑使用解释器模式。
(3) 一个简单语法需要解释的场景,可以考虑使用解释器模式。对于简单语法,由于其文法规则较简单,使用解释器模式要优于语法分析程序。
优缺点
解释器模式有以下优点:
(1) 可扩展性好。因为该模式使用类来表示文法规则,可以使用继承来改变或扩展该文法。
(2) 易于实现简单的文法。定义抽象语法树各个节点的类的实现大体相似。
但是该模式也存在以下缺点:
(1) 可利用场景比较少。
(2) 对于复杂的文法比较难维护。包含许多规则的文法可能难以管理和维护。
(3) 会引起类膨胀。随着文法规则的复杂化,类的规模也会随之膨胀。
(4) 使用了大量的循环和递归,需要考虑效率问题。
参考
《设计模式 可复用面向对象软件的基础》 Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides 著, 李英军, 马晓星等译
https://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/mediator.html 解释器模式
https://refactoringguru.cn/design-patterns/mediator 解释器模式
https://www.runoob.com/design-pattern/mediator-pattern.html 解释器模式
https://www.cnblogs.com/adamjwh/p/10959987.html 简说设计模式——解释器模式
https://springframework.guru/gang-of-four-design-patterns/interpreter-pattern/ Interpreter Pattern