设计模式系列:搞懂解释器模式,学会解释语言

解释器(Interpreter)模式的定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。属于行为型模式。

解释器模式的结构:

文法:文法是用于描述语言的语法结构的形式规则,比如C = A + B。

终结符:文法中的变量,比如公式C = A + B中A、B就是终结符。

非终结符:文法中的运算符或其他关键字,比如公式C = A + B中 "+" 就是终结符。

解释器模式主要包含以下4个角色。

  1. 抽象表达式(Abstract Expression):定义解释器的接口,主要包含解释方法 interpret()。
  2. 终结符表达式(Terminal Expression):抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
  3. 非终结符表达式(Nonterminal Expression):抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
  4. 上下文环境(Context):包含解释器之外的全局信息,一般用来传递被所有解释器共享的数据。

解释器模式的实现:

//抽象表达式类
public interface Expression {
    int interpret();
}

//终结符表达式
public class TerminalExpression implements Expression{
    private int num;

    public TerminalExpression(int num){
        this.num = num;
    }

    @Override
    public int interpret() {
        return num;
    }
}

//非终结符表达式
public class AddExpression implements Expression{
    private Expression leftNum;

    private Expression rightNum;

    public AddExpression(Expression leftNum,Expression rightNum){
        this.leftNum = leftNum;
        this.rightNum = rightNum;
    }

    @Override
    public int interpret() {
        return leftNum.interpret() + rightNum.interpret();
    }
}

//非终结符表达式
public class SubExpression implements Expression{
    private Expression leftNum;

    private Expression rightNum;

    public SubExpression(Expression leftNum, Expression rightNum){
        this.leftNum = leftNum;
        this.rightNum = rightNum;
    }

    @Override
    public int interpret() {
        return leftNum.interpret() - rightNum.interpret();
    }
}

//非终结符表达式
public class MultiExpression implements Expression{
    private Expression leftNum;

    private Expression rightNum;

    public MultiExpression(Expression leftNum, Expression rightNum){
        this.leftNum = leftNum;
        this.rightNum = rightNum;
    }

    @Override
    public int interpret() {
        return leftNum.interpret() * rightNum.interpret();
    }
}

//非终结符表达式
public class DivExpression implements Expression{
    private Expression leftNum;

    private Expression rightNum;

    public DivExpression(Expression leftNum, Expression rightNum){
        this.leftNum = leftNum;
        this.rightNum = rightNum;
    }

    @Override
    public int interpret() {
        return leftNum.interpret() / rightNum.interpret();
    }
}

//上下文环境
public class Context {
    public int operation(String formula){
        int result = 0;
        if(formula.contains("+")){
            result = addOperation(formula);
        }else if(formula.contains("-")){
            result = subOperation(formula);
        }else if(formula.contains("*")){
            result = multiOperation(formula);
        }else if(formula.contains("/")){
            result = divOperation(formula);
        }
        return result;
    }

    private int addOperation(String formula) {
        String s[] = formula.split("\\+");
        Expression leftNum = new TerminalExpression(Integer.parseInt(s[0].trim()));
        Expression rightNum = new TerminalExpression(Integer.parseInt(s[1].trim()));
        Expression result = new AddExpression(leftNum,rightNum);
        return result.interpret();
    }

    private int subOperation(String formula) {
        String s[] = formula.split("-");
        Expression leftNum = new TerminalExpression(Integer.parseInt(s[0].trim()));
        Expression rightNum = new TerminalExpression(Integer.parseInt(s[1].trim()));
        Expression result = new SubExpression(leftNum,rightNum);
        return result.interpret();
    }

    private int multiOperation(String formula) {
        String s[] = formula.split("\\*");
        Expression leftNum = new TerminalExpression(Integer.parseInt(s[0].trim()));
        Expression rightNum = new TerminalExpression(Integer.parseInt(s[1].trim()));
        Expression result = new MultiExpression(leftNum,rightNum);
        return result.interpret();
    }

    private int divOperation(String formula) {
        String s[] = formula.split("/");
        Expression leftNum = new TerminalExpression(Integer.parseInt(s[0].trim()));
        Expression rightNum = new TerminalExpression(Integer.parseInt(s[1].trim()));
        Expression result = new DivExpression(leftNum,rightNum);
        return result.interpret();
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        Context context = new Context();
        int addResult = context.operation("12 + 13");
        System.out.println("12 + 13 = " + addResult);
        int subResult = context.operation("12 - 7");
        System.out.println("12 + 7 = " + subResult);
        int multiResult = context.operation("12 * 13");
        System.out.println("12 * 13 = " + multiResult);
        int divResult = context.operation("39 / 13");
        System.out.println("39 / 13 = " + divResult);
    }
}

解释器模式的结构图:

解释器模式在源码中的应用:

  • Jdk中对正则表达式进行解析是Pattern类。
  • Spring中提供的ExpressionParser接口的实现类。

Java提供的解释器模式类库:在项目开发中,如果要对数据表达式进行分析与计算,可以使用 Java 提供的强大的数学公式解析器:Expression4J、MESP(Math Expression String Parser) 和 Jep(Java expression parser) 等,它们可以解释一些复杂的文法,功能强大,使用简单。

解释器模式的优点:

  1. 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
  2. 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。

解释器模式的缺点:

  1. 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢。
  2. 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以维护。

解释器模式的应用场景:

前面介绍了解释器模式的结构与特点,下面分析它的应用场景。

  1. 当语言的文法较为简单,且执行效率不是关键问题时。
  2. 当问题重复出现,且可以用一种简单的语言来进行表达时。
  3. 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树,如 XML 文档解释。
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风雨编码路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值