Java-解释器-设计模式(二十二)

说明

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

UML
在这里插入图片描述
角色:
抽象表达式(AbstractExpression)角色:约定解释器的解释操作,主要是一个interpret()方法。
终结符表达式(TerminalExpression)角色:用来实现文法中和终结符相关的解释操作,不再包含其它的解释器,如果用组合模式来构建抽象语法树的话,就相当于组合模式中的叶子对象,可以有多种终结符解释器。
非终结符表达式(NonterminalExpression)角色:用来实现文法中和非终结符相关的解释操作,通常一个解释器对应一个语法规则,可以包含其它的解释器,如果用组合模式来构建抽象语法树的话,就相当于组合模式中的组合对象,可以有多种非终结符解释器。
环境(Context)角色:也称“上下文”,常用HashMap来代替,通常包含解释器之外的一些全局信息(解释器需要的数据,或是公共的功能)。
客户端(Client)角色:构建文法表示的抽象语法树(Abstract Syntax Tree,该抽象语法树由终结符表达式和非终结符表达式的实例装配而成),并调用解释操作interpret()方法。

代码

我在之前设计模式文章中的很多例子都是参考的《大话设计模式》这本书中的例子来写的,但这次感觉书中写的多少有些复杂了,下面这篇博客的例子感觉更易懂,并且我参考这个例子又写了一遍,感觉理解起来还是挺有帮助的。
下面例子参考的原博地址,点击访问。

一个建议加减法计算器的例子,代码如下。

抽象表达式

/**
 * 抽象表达式
 *
 * @author ctl
 * @date 2021/2/1
 */
public interface AbstractExpression {

    // 用map来代替context
    int interpret(Map<String, Integer> var);
}

终结符表达式

/**
 * 终结符表达式,代表参加运算的元素对象
 *
 * @author ctl
 * @date 2021/2/1
 */
public class VarExpression implements AbstractExpression {

    private String key;

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

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

非终结符表达式

/**
 * 非终结符表达式,运算符(此处为加法和减法)的抽象父类,真正的解释操作由其子类来实现
 *
 * @author ctl
 * @date 2021/2/1
 */
public abstract class SymbolExpression implements AbstractExpression {

    protected AbstractExpression left;
    protected AbstractExpression right;

    // 非终结符表达式的解释操作只关心自己左右两个表达式的结果
    public SymbolExpression(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }
}

加法表达式

/**
 * 加法表达式
 *
 * @author ctl
 * @date 2021/2/1
 */
public class AddExpression extends SymbolExpression {

    public AddExpression(AbstractExpression left, AbstractExpression right) {
        super(left, right);
    }

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

减法表达式

/**
 * 减法表达式
 *
 * @author ctl
 * @date 2021/2/1
 */
public class SubExpression extends SymbolExpression {

    public SubExpression(AbstractExpression left, AbstractExpression right) {
        super(left, right);
    }

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

计算器

/**
 * @author ctl
 * @date 2021/2/1
 */
public class Calculator {

    private AbstractExpression expression;

    /**
     * 对公式进行解析操作,构件语法树,+ -这种操作符是树中的枝干,具体的数字是树的叶子节点
     *
     * @param expStr 输入的公式
     */
    public Calculator(String expStr) {
        // 定义一个栈,安排运算的先后顺序
        Stack<AbstractExpression> stack = new Stack<>();
        // 表达式拆分为字符数组
        char[] charArray = expStr.toCharArray();
        // 运算
        AbstractExpression left;
        AbstractExpression right;
        for (int i = 0; i < charArray.length; i++) {
            switch (charArray[i]) {
                case '+':
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new AddExpression(left, right));
                    break;
                case '-':
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new SubExpression(left, right));
                    break;
                default:
                    stack.push(new VarExpression(String.valueOf(charArray[i])));
            }
        }
        // 把运算结果抛出来
        this.expression = stack.pop();
    }

    // 计算结果
    public int calculate(Map<String, Integer> var) {
        return this.expression.interpret(var);
    }
}

测试类

/**
 *
 * @author ctl
 * @date 2021/2/1
 */
public class InterpreterMain {

    public static void main(String[] args) {
        // 这里使用map来当作context的角色
        Map<String, Integer> ctx = new HashMap<>();
        ctx.put("a", 10);
        ctx.put("b", 20);
        ctx.put("c", 30);
        ctx.put("d", 40);
        ctx.put("e", 50);
        ctx.put("f", 60);
        Calculator calc;

        calc = new Calculator("a+b-c");
        int result = calc.calculate(ctx);
        System.out.println("Result of a+b-c: " + result);

        calc = new Calculator("d-a-b+c");
        result = calc.calculate(ctx);
        System.out.println("Result of d-a-b+c: " + result);
    }
}

结果
在这里插入图片描述

总结

当有一个语言需要解释执行,并且可以将该语言中的句子表示为一个抽象语法树时,可以使用解释器模式。如sql解析,编程语言的解析等等。

解释器模式可以容易地改变和扩展文法,因为该模式使用类来表示文法规则,可以使用继承继承来改变或扩展该文法。也比较容易实现文法,因为定义抽象语法树的各个节点的类的实现大体相似,这些类都易于直接编写。

当然也有不足之处,解释器模式为文法中的每一条规则都至少定义了一个类,因此包含很多规则的文法可能难以维护和管理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值