解释器(Interpreter)模式的定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。属于行为型模式。
解释器模式的结构:
文法:文法是用于描述语言的语法结构的形式规则,比如C = A + B。
终结符:文法中的变量,比如公式C = A + B中A、B就是终结符。
非终结符:文法中的运算符或其他关键字,比如公式C = A + B中 "+" 就是终结符。
解释器模式主要包含以下4个角色。
- 抽象表达式(Abstract Expression):定义解释器的接口,主要包含解释方法 interpret()。
- 终结符表达式(Terminal Expression):抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
- 非终结符表达式(Nonterminal Expression):抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
- 上下文环境(Context):包含解释器之外的全局信息,一般用来传递被所有解释器共享的数据。
解释器模式的实现:
//抽象表达式类
public interface Expression {
int interpret();
}
//终结符表达式
public class TerminalExpression implements Expression{
private int num;
public TerminalExpression(int num){
this.num = num;
}
@Override
public int interpret() {
return num;
}
}
//非终结符表达式
public class AddExpression implements Expression{
private Expression leftNum;
private Expression rightNum;
public AddExpression(Expression leftNum,Expression rightNum){
this.leftNum = leftNum;
this.rightNum = rightNum;
}
@Override
public int interpret() {
return leftNum.interpret() + rightNum.interpret();
}
}
//非终结符表达式
public class SubExpression implements Expression{
private Expression leftNum;
private Expression rightNum;
public SubExpression(Expression leftNum, Expression rightNum){
this.leftNum = leftNum;
this.rightNum = rightNum;
}
@Override
public int interpret() {
return leftNum.interpret() - rightNum.interpret();
}
}
//非终结符表达式
public class MultiExpression implements Expression{
private Expression leftNum;
private Expression rightNum;
public MultiExpression(Expression leftNum, Expression rightNum){
this.leftNum = leftNum;
this.rightNum = rightNum;
}
@Override
public int interpret() {
return leftNum.interpret() * rightNum.interpret();
}
}
//非终结符表达式
public class DivExpression implements Expression{
private Expression leftNum;
private Expression rightNum;
public DivExpression(Expression leftNum, Expression rightNum){
this.leftNum = leftNum;
this.rightNum = rightNum;
}
@Override
public int interpret() {
return leftNum.interpret() / rightNum.interpret();
}
}
//上下文环境
public class Context {
public int operation(String formula){
int result = 0;
if(formula.contains("+")){
result = addOperation(formula);
}else if(formula.contains("-")){
result = subOperation(formula);
}else if(formula.contains("*")){
result = multiOperation(formula);
}else if(formula.contains("/")){
result = divOperation(formula);
}
return result;
}
private int addOperation(String formula) {
String s[] = formula.split("\\+");
Expression leftNum = new TerminalExpression(Integer.parseInt(s[0].trim()));
Expression rightNum = new TerminalExpression(Integer.parseInt(s[1].trim()));
Expression result = new AddExpression(leftNum,rightNum);
return result.interpret();
}
private int subOperation(String formula) {
String s[] = formula.split("-");
Expression leftNum = new TerminalExpression(Integer.parseInt(s[0].trim()));
Expression rightNum = new TerminalExpression(Integer.parseInt(s[1].trim()));
Expression result = new SubExpression(leftNum,rightNum);
return result.interpret();
}
private int multiOperation(String formula) {
String s[] = formula.split("\\*");
Expression leftNum = new TerminalExpression(Integer.parseInt(s[0].trim()));
Expression rightNum = new TerminalExpression(Integer.parseInt(s[1].trim()));
Expression result = new MultiExpression(leftNum,rightNum);
return result.interpret();
}
private int divOperation(String formula) {
String s[] = formula.split("/");
Expression leftNum = new TerminalExpression(Integer.parseInt(s[0].trim()));
Expression rightNum = new TerminalExpression(Integer.parseInt(s[1].trim()));
Expression result = new DivExpression(leftNum,rightNum);
return result.interpret();
}
}
//测试类
public class Test {
public static void main(String[] args) {
Context context = new Context();
int addResult = context.operation("12 + 13");
System.out.println("12 + 13 = " + addResult);
int subResult = context.operation("12 - 7");
System.out.println("12 + 7 = " + subResult);
int multiResult = context.operation("12 * 13");
System.out.println("12 * 13 = " + multiResult);
int divResult = context.operation("39 / 13");
System.out.println("39 / 13 = " + divResult);
}
}
解释器模式的结构图:
解释器模式在源码中的应用:
- Jdk中对正则表达式进行解析是Pattern类。
- Spring中提供的ExpressionParser接口的实现类。
Java提供的解释器模式类库:在项目开发中,如果要对数据表达式进行分析与计算,可以使用 Java 提供的强大的数学公式解析器:Expression4J、MESP(Math Expression String Parser) 和 Jep(Java expression parser) 等,它们可以解释一些复杂的文法,功能强大,使用简单。
解释器模式的优点:
- 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
- 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
解释器模式的缺点:
- 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢。
- 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以维护。
解释器模式的应用场景:
前面介绍了解释器模式的结构与特点,下面分析它的应用场景。
- 当语言的文法较为简单,且执行效率不是关键问题时。
- 当问题重复出现,且可以用一种简单的语言来进行表达时。
- 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树,如 XML 文档解释。