解释器模式

1 四则运算问题

现在有一个四则运算问题需要用代码实现:计算形如a+b-c的值,其中表达式的字母不能为空,并且每个字母都有数字与它对应,最后求出结果。
传统的方法是写一个方法,接收表达式的形式,然后根据用户的输入数值进行解析,最终计算出结果。但是这种方法不利于扩展,即加入新的运算符需要新增对应方法来解析,容易造成程序的混乱。为了更好地解决这个问题,可以考虑使用解释器模式。

2 解释器模式介绍

在编译原理中,一个算术表达式通过词法分析器形成词法单元,然后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以称之为解释器。
解释器模式(Interpreter Pattern):给定一个语言(表达式),定义它的文法的一种表示,并形成一个解释器,使用该解释器来解释语言中的句子。
角色分析:
1)Context:环境上下文角色,包含解释器之外额全局信息
2)AbstractExpression:抽象表达式,声明一个抽象的解释操作,这个方法为抽象语法树中所有的节点共享
3)TerminalExpression:终结符表达式,实现与文法中的终结符相关的解释操作
4)NonTerminalExpression:非终结符表达式,为文法中的非终结符实现解释操作
5)Client:调用方,Context和TerminalExpression可以在这里输入


解释器模式类图

3 代码实现

首先有一个抽象表达式Expression:

// 表达式抽象类,通过Map获取到变量的值
public abstract class Expression {
    // 解释公式和数值
    public abstract int interpreter(Map<String, Integer> var);
}

终结符表达式继承自Expression,对应到需求中的字母:

public class VarExpression extends Expression {
    private String key; // key=a,b,c...

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

    @Override
    public int interpreter(Map<String, Integer> var) {
        return var.get(key);
    }
}

非终结符表达式基类,对应运算符(+、-):

// 抽象的运算符号解析器,每个运算符号都只和自己左右两个数字或表达式有关系
public abstract class SymbolExpression extends Expression{
    protected Expression left;
    protected Expression right;

    public SymbolExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }
}

加法运算符表达式类:

public class AddExpression extends SymbolExpression {
    public AddExpression(Expression left, Expression right) {
        super(left, right);
    }

    @Override
    public int interpreter(Map<String, Integer> var) {
        return left.interpreter(var) + right.interpreter(var);
    }
}

减法运算符表达式类:

public class SubExpression extends SymbolExpression {
    public SubExpression(Expression left, Expression right) {
        super(left, right);
    }

    @Override
    public int interpreter(Map<String, Integer> var) {
        return left.interpreter(var) - right.interpreter(var);
    }
}

环境上下文类:

public class Calculator {
    private Expression expression;

    public Calculator(String expStr) {
        Stack<Expression> stack = new Stack<>();
        char[] chars = expStr.toCharArray();

        Expression left = null;
        Expression right = null;
        for (int i = 0; i < chars.length; i++) {
            switch (chars[i]) {
                case '+':
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(chars[++i]));
                    stack.push(new AddExpression(left, right));
                    break;
                case '-':
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(chars[++i]));
                    stack.push(new SubExpression(left, right));
                    break;
                default:
                    stack.push(new VarExpression(String.valueOf(chars[i])));
            }
        }
        expression = stack.pop();
    }

    public int run(Map<String, Integer> var) {
        return expression.interpreter(var);
    }
}

Client调用:

public class Client {
    public static void main(String[] args) {
        String expStr = "a+b-c";
        Map<String, Integer> var = new HashMap<>();
        var.put("a", 2);
        var.put("b", 3);
        var.put("c", 4);
        Calculator calculator = new Calculator(expStr);
        System.out.println("运算结果:" + expStr + " = " + calculator.run(var));
    }
}

输出结果:

运算结果:a+b-c = 1
四则运算实现类图

4 解释器模式的特点

1)当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象的语法树,此时可以考虑使用解释器模式,让程序具有良好的扩展性。
2)解释器模式的使用场景:编译器、运算表达式、正则表达式等。
3)使用解释器可能会带来类膨胀,即Context类可能会非常复杂,另外解释器模式采用递归调用的方式,将会导致调试困难、执行效率低下等问题。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容