解释器模式

3+4-5,经历的过程向计算器输入表达式公式,然后由计算器解释该公式再计算出结果。这个就是典型的解释器模式,还有我们利用正则表达式进行匹配字符串也是解释器模式的一种应用。

解释器模式的适用性

  • 可以将一个需要解释执行语言中的句子,表示为一个抽象语法树。
  • 一些重复出现的问题可以通过一种简单的语言来表达。
  • 一个简单语法需要解释的场景。

解释器模型结构

  

java解释器源码 java编写一个解释器_java解释器源码

解释器模式总共包含以下四类角色:

抽象表达式(AbstractExpression):声明一个抽象的解释操作,该操作被抽象语法树种所有的节点共享。

终结符表达式(TerminalExpression):实现与文法中终结符相关联的解释操作,一个句子中每个终结符都需要该类的一个实例。(例如表达式3+4-5中3、4、5就是终结符)

非终结符表达式(NonterminalExpression):对文法中的每一条规则都需要一个非终结表达式类,为文法中的非终结符实现解释操作。(例如表达式3+4-5中的+、-)

上下文(Context):包含解释器之外的上下文。

 

解释器模式的示例

本文实现一个简易的计算加减法整形的计算例子,首先还是定义一个抽象表达式接口

public interface AbstractExpression {
    int interpreter(Context context);
}

  其次定义表示整形数字的终结符表达式类和表示加减号的非终结符表达式类:

终结符表达式类

public class TerminalExpression implements AbstractExpression {
    private String key;

    public TerminalExpression(String key) {
        this.key = key;
    }

    @Override
    public int interpreter(Context context) {
        return context.get(key);
    }
}

非终结符表达式类

public class NonterminalExpression implements AbstractExpression {
    protected AbstractExpression abstractExpression1;
    protected AbstractExpression abstractExpression2;

    public NonterminalExpression(AbstractExpression abstractExpression1, AbstractExpression abstractExpression2) {
        this.abstractExpression1 = abstractExpression1;
        this.abstractExpression2 = abstractExpression2;
    }

    @Override
    public int interpreter(Context context) {
        return 0;
    }
}

定义一个加法非终结符表达式类和减法非终结符表达式类继承NonterminalExpression类

public class PlusExpression extends NonterminalExpression {
    public PlusExpression(AbstractExpression abstractExpression1, AbstractExpression abstractExpression2) {
        super(abstractExpression1, abstractExpression2);
    }

    @Override
    public int interpreter(Context context) {
        return this.abstractExpression1.interpreter(context) + this.abstractExpression2.interpreter(context);
    }
}
public class SubExpression extends NonterminalExpression {
    public SubExpression(AbstractExpression abstractExpression1, AbstractExpression abstractExpression2) {
        super(abstractExpression1, abstractExpression2);
    }

    @Override
    public int interpreter(Context context) {
        return this.abstractExpression1.interpreter(context) - this.abstractExpression2.interpreter(context);
    }
}

定义一个上下文类,存储要计算的上下文信息

public class Context {
    private HashMap<String, Integer> hashMap = new HashMap<String, Integer>();

    public void add(String key, int value) {
        hashMap.put(key, value);
    }

    public int get(String key) {
        return hashMap.get(key);
    }
}

定义计算器类,接收输入的字符串,计算结果

public class Calculator {
    private AbstractExpression abstractExpression;

    public Calculator(String expStr) {
        Stack<AbstractExpression> stack = new Stack<>();
        char[] chars = expStr.toCharArray();
        AbstractExpression left;
        AbstractExpression right;
        for (int i = 0; i < chars.length; i++) {
            switch (chars[i]) {
                case '+':
                    left = stack.pop();
                    right = new TerminalExpression(String.valueOf(chars[++i]));
                    stack.push(new PlusExpression(left, right));
                    break;
                case '-':
                    left = stack.pop();
                    right = new TerminalExpression(String.valueOf(chars[++i]));
                    stack.push(new SubExpression(left, right));
                    break;
                default:
                    stack.push(new TerminalExpression(String.valueOf(chars[i])));
                    break;
            }
        }
        this.abstractExpression=stack.pop();
    }

    public int calcu(Context context){
        return abstractExpression.interpreter(context);
    }
}

客户端调用,这里为了简单,就不再通过识别输入字符串的方式对上下文赋值,而是直接赋值

public class Client {
    public static void main(String[] args) {
        /*识别输入字符串转换为Context中的上下文就不实现了直接赋值*/
        Context context=new Context();
        context.add("3",3);
        context.add("4",4);
        context.add("5",5);
        Calculator calculator=new Calculator("3+5-4");
        System.out.println(calculator.calcu(context));
    }
}

 

总结

  解释器模式易于改变和扩展文法,同时也方便实现文法可实现对有许多次相同的需要解释的语言的请求的复用,但是从上述的例子就可以看出,如果要新增一种非终结符表达式则需新增一个类,这样会硬气类的逐步膨胀导致生成许多的类,其次该模式采用了大量的递归调用,使得调用较复杂不好调试以及维护,同时程序解释的执行效率也是需要重视的一个问题。