天天看点

设计模式-解释器模式(Interpreter Pattern)

推荐:​​Java设计模式汇总​​

解释器模式

定义

Given a language,define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

类型

行为型。

角色

  • AbstractExpression(抽象表达式):在抽象表达式中声明抽象的解释操作,它是所有终结符表达式和非终结符表达式的公共父类。
  • TerminalExpression(终结符表达式):终结符表达式是抽象表达式的子类,它实现了与文法中的终结符相关联的解释操作,在句子中的每一个终结符都是该类的一个实例。通常在一个解释器模式中只有少数几个终结符表达式类,它们的实例可以通过非终结符表达式组成较为复杂的句子。
  • NonterminalExpression(非终结符表达式):非终结符表达式也是抽象表达式的子类,它实现了文法中非终结符的解释操作,由于在非终结符表达式中可以包含终结符表达式,也可以继续包含非终结符表达式,因此其解释操作一般通过递归的方式来完成。
  • Context(环境类):环境类又称为上下文类,它用于存储解释器之外的一些全局信息,通常它临时存储了需要解释的语句。

例子

这里举计算后缀表达式的例子,为了更加简化,这里只处理两种运算符​​

​+​

​​、​

​*​

​,没有括号,并且默认后缀表达式是正确的。

Interpreter接口(抽象表达式)。

package com.kaven.design.pattern.behavioral.interpreter;

public interface Interpreter {
    int interpret();
}      

NumberInterpreter类(非终结符表达式),实现了Interpreter接口。

package com.kaven.design.pattern.behavioral.interpreter;

public class NumberInterpreter implements Interpreter {
    private int number;
    public NumberInterpreter(int number){
        this.number = number;
    }
    public NumberInterpreter(String number){
        this.number = Integer.parseInt(number);
    }
    public int interpret() {
        return this.number;
    }
}      

AddInterpreter类(终结符表达式),实现了Interpreter接口。

package com.kaven.design.pattern.behavioral.interpreter;

public class AddInterpreter implements Interpreter {

    private Interpreter firstExpression,secondExpression;

    public AddInterpreter(Interpreter firstExpression, Interpreter secondExpression) {
        this.firstExpression = firstExpression;
        this.secondExpression = secondExpression;
    }

    public int interpret() {
        return this.firstExpression.interpret()+this.secondExpression.interpret();
    }

    public String toString(){
        return "+";
    }
}      

MultiInterpreter类(终结符表达式),实现了Interpreter接口。

package com.kaven.design.pattern.behavioral.interpreter;

public class MultiInterpreter implements Interpreter {
    private Interpreter firstExpression,secondExpression;

    public MultiInterpreter(Interpreter firstExpression, Interpreter secondExpression) {
        this.firstExpression = firstExpression;
        this.secondExpression = secondExpression;
    }

    public int interpret() {
        return this.firstExpression.interpret()*this.secondExpression.interpret();
    }

    public String toString(){
        return "*";
    }
}      

OperatorUtil类(操作工具类)。

package com.kaven.design.pattern.behavioral.interpreter;

public class OperatorUtil {
    public static boolean isOperator(String symbol){
        return (symbol.equals("+") || symbol.equals("*"));
    }

    public static Interpreter getExpressionObject(Interpreter firstExpression ,
                                                  Interpreter secondExpression ,
                                                  String symbol){
        if(symbol.equals("+")){
            return new AddInterpreter(firstExpression , secondExpression);
        }
        else if(symbol.equals("*")){
            return new MultiInterpreter(firstExpression , secondExpression);
        }
        return null;
    }
}      

KavenExpressionParser类,这里使用了上面实现的解释器模式相关类和​

​Stack​

​实现了计算后缀表达式的逻辑,计算后缀表达式的算法应该是数据结构的基础吧,这里就不讲了。

package com.kaven.design.pattern.behavioral.interpreter;

import java.util.Stack;

public class KavenExpressionParser {
    private Stack<Interpreter> stack = new Stack<Interpreter>();

    public int parse(String str){
        String[] strItemArray = str.split(" ");
        for(String symbol : strItemArray){
            if(!OperatorUtil.isOperator(symbol)){
                Interpreter numberExpression = new NumberInterpreter(symbol);
                stack.push(numberExpression);
                System.out.println(String.format("入栈:%d",numberExpression.interpret()));
            }
            else{
                // 是运算符号可以计算
                Interpreter firstExpression = stack.pop();
                Interpreter secondExpression = stack.pop();
                System.out.println(String.format("出栈:%d 和 %d",
                        firstExpression.interpret(),secondExpression.interpret()));
                Interpreter operator = OperatorUtil.getExpressionObject(firstExpression ,
                        secondExpression , symbol);
                System.out.println(String.format("应用运算符: %s",operator));
                int result = operator.interpret();
                NumberInterpreter resultExpression = new NumberInterpreter(result);
                stack.push(resultExpression);
                System.out.println(String.format("阶段结果入栈:%d",resultExpression.interpret()));
            }
        }
        int result  = stack.pop().interpret();
        return result;
    }
}      

Expression类(环境类),存储后缀表达式。

package com.kaven.design.pattern.behavioral.interpreter;

public class Expression {
    private  String expression;

    public Expression(String expression) {
        this.expression = expression;
    }

    public  String getExpression() {
        return this.expression;
    }
}      

应用层代码:

package com.kaven.design.pattern.behavioral.interpreter;

public class Test {
    public static void main(String[] args) {
        Expression expression = new Expression("6 100 11 + *");
        KavenExpressionParser expressionParser = new KavenExpressionParser();
        int result = expressionParser.parse(expression.getExpression());
        System.out.println("解释器计算结果"+result);
    }
}      

输出:

入栈:6
入栈:100
入栈:11
出栈:11 和 100
应用运算符: +
阶段结果入栈:111
出栈:111 和 6
应用运算符: *
阶段结果入栈:666
解释器计算结果666      

这里便完成了一个简单的解释器模式例子。

适用场景

  • 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
  • 一些重复出现的问题可以用一种简单的语言进行表达。
  • 执行效率不是关键问题。高效的解释器通常不是通过直接解释抽象语法树来实现的,而是需要将它们转换成其他形式,使用解释器模式的执行效率并不高。

优点

  • 易于改变和扩展文法。由于在解释器模式中使用类表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
  • 实现文法较为容易。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂,还可以通过一些工具自动生成节点类代码。
  • 解释器模式会引起类膨胀。
  • 解释器模式将会导致系统比较复杂, 为维护带来了非常多的麻烦。
  • 执行效率低。由于在解释器模式中一般采用了大量的循环和递归调用(我们的例子是使用栈来代替递归),因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。

继续阅读