解释器模式——简单语言的实现


Demo 地址: https://github.com/ooblee/HelloDesignPattern

1. 定义

解释器模式(Interpreter Pattern):定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的“语言”是指使用规定格式和语法的代码。解释器模式是一种类行为型模式。

自定义语言,定义文法规则,并用文法规则解释语言中的句子。

具体实现的时候,会建立抽象语法树。

2. 设计

主要角色:

  • 抽象表达式(Abstract Expression),声明了解释操作,所有表达式的父类。
  • 终结符表达式(Terminal Expression),声明与终结符相关的解释操作。
  • 非终结符表达式(Nonterminal Expression),声明与非终结符相关的解释操作。可以继续包含非终结符和终结符,进行递归或者循环处理。
  • 上下文环境(Context),存储需要解释的语句,或者一些全局变量。

类图如下:

解释器模式-类图

这里展示一个加减法表达式的实现。

抽象表达式,定义解释操作,返回操作后的值:

public interface IExpression {

    int interpret();
}

加法表达式,属于非终止符表达式,解释的时候对左边和右边的表达式的结果进行相加:

public class PlusExpression implements IExpression {

    private IExpression left;
    private IExpression right;

    public PlusExpression(IExpression left, IExpression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret() {
        return left.interpret() + right.interpret();
    }
}

减法表达式,属于非终止符表达式,解释的时候对左边和右边的表达式的结果进行相减:

public class MinusExpression implements IExpression {

    private IExpression left;
    private IExpression right;

    public MinusExpression(IExpression left, IExpression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret() {
        return left.interpret() - right.interpret();
    }
}

数字表达式,属于终止符表达式,为整个抽象语法树的叶子节点,解释的时候把数字字符转为具体的数字:

public class NumberExpression implements IExpression {

    public String number;

    public NumberExpression(String number) {
        this.number = number;
    }

    @Override
    public int interpret() {
        return Integer.valueOf(number);
    }
}

表达式的处理类,如果复杂些的表达式,该处理类还会处理一些全局变量。这里没有这样的需求。处理待解释语句的时候,使用栈来构建抽象语法树。每次解析出来的表达式均入栈。如果非终止符表达式需要使用左边表达式的话,直接出栈弹出栈顶的表达式给非终止符表达式。

public class MathHandler {

    public MathHandler() {
    }

    public int handle(String sequence) {
        String[] words = sequence.split(" ");

        Stack<IExpression> stack = new Stack<>();
        for (int i = 0; i < words.length; i++) {
            String word = words[i];
            if (word.equalsIgnoreCase("+")) {
                IExpression left = stack.pop();
                IExpression right = new NumberExpression(words[++i]);
                IExpression plus = new PlusExpression(left, right);
                stack.push(plus);
            } else if (word.equalsIgnoreCase("-")) {
                IExpression left = stack.pop();
                IExpression right = new NumberExpression(words[++i]);
                IExpression minus = new MinusExpression(left, right);
                stack.push(minus);
            } else {
                IExpression expression = new NumberExpression(word);
                stack.push(expression);
            }
        }
        IExpression expression = stack.pop();
        return expression.interpret();
    }
}

调用的方法,我们传入一些语句,看是否能够正确进行解释:

public class TestInterpret {

    public static void main(String[] args) {

        MathHandler mathHandler = new MathHandler();

        String sequence = "1 + 1";
        System.out.println(mathHandler.handle(sequence));

        sequence = "1 + 2 + 3 + 4 + 5";
        System.out.println(mathHandler.handle(sequence));

        sequence = "1 + 2 + 3 - 4 - 5";
        System.out.println(mathHandler.handle(sequence));
    }
}

输出正常:

2
15
-3

3. 应用

适用场景:

  • 对解释执行的语句抽象成语法树。
  • 对重复出现的问题用简单的语句表达。
  • 对执行效率要求不高。

3.1. JDK 的 Pattern

4. 特点

4.1. 优点

  • 易修改和扩展,可以用继承的方式来对文法类进行扩展。
  • 实现简单。每一个表达式节点的实现方式类似。
  • 容易新增表达式。只需增加一个新的终结符表达式或非终结符表达式类。

4.2. 缺点

  • 难以实现复杂文法。会导致类膨胀。
  • 执行效率低。存在大量的循环和递归。
发布了61 篇原创文章 · 获赞 43 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/firefile/article/details/90314430
今日推荐