java23种设计模式(二十三) -- 解释器模式(行为设计模式)

解释器模式

简介

解释器模式是类的行为模式。给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。

解释器模式是指给定一个使用规定格式和语法的语言,并且建立一个解释器来解释该语言中的句子。解释器本身就是一种按照规定的语法进行解析的方案,但是总体来说也是一种使用频率相对较低但学习难度较大的设计模式。

结构

  1. 抽象表达式(Expression)角色:声明一个所有的具体表达式角色都需要实现的抽象接口。这个接口主要是一个interpret()方法,称做解释操作。
  2. 终结符表达式(Terminal Expression)角色:实现了抽象表达式角色所要求的接口,主要是一个interpret()方法;文法中的每一个终结符都有一个具体终结表达式与之相对应。比如有一个简单的公式R=R1+R2,在里面R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。
  3. 非终结符表达式(Nonterminal Expression)角色:文法中的每一条规则都需要一个具体的非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2中,“+"就是非终结符,解析“+”的解释器就是一个非终结符表达式。
  4. 环境(Context)角色:这个角色的任务一般是用来存放文法中各个终结符所对应的具体值,比如R=R1+R2,我们给R1赋值100,给R2赋值200。这些信息需要存放到环境角色中,很多情况下我们使用Map来充当环境角色就足够了。

代码实现

项目结构

在这里插入图片描述
Expression接口

/**
 * 表达式的统一定义.
 */
public interface Expression {
 
    int interpret();
 
    @Override
    String toString();
}

表达式

/**
 * 加号
 */
public class PlusExpression implements Expression {
 
    private Expression leftExpression;
    private Expression rightExpression;
 
    public PlusExpression(Expression leftExpression, Expression rightExpression) {
        this.leftExpression = leftExpression;
        this.rightExpression = rightExpression;
    }
 
    @Override
    public int interpret() {
        return leftExpression.interpret() + rightExpression.interpret();
    }
 
    @Override
    public String toString() {
        return "+";
    }
}

/**
 * 减号
 */
public class MinusExpression implements Expression {
 
    private Expression leftExpression;
    private Expression rightExpression;
 
    public MinusExpression(Expression leftExpression, Expression rightExpression) {
        this.leftExpression = leftExpression;
        this.rightExpression = rightExpression;
    }
 
    @Override
    public int interpret() {
        return leftExpression.interpret() - rightExpression.interpret();
    }
 
    @Override
    public String toString() {
        return "-";
    }
 
}

/**
 * 乘号
 */
public class MultiplyExpression implements Expression {
 
    private Expression leftExpression;
    private Expression rightExpression;
 
    public MultiplyExpression(Expression leftExpression, Expression rightExpression) {
        this.leftExpression = leftExpression;
        this.rightExpression = rightExpression;
    }
 
    @Override
    public int interpret() {
        return leftExpression.interpret() * rightExpression.interpret();
    }
 
    @Override
    public String toString() {
        return "*";
    }
 
}

/**
 * 数字
 */
public class NumberExpression implements Expression {
 
    private int number;
 
    public NumberExpression(int number) {
        this.number = number;
    }
 
    public NumberExpression(String s) {
        this.number = Integer.parseInt(s);
    }
 
    @Override
    public int interpret() {
        return number;
    }
 
    @Override
    public String toString() {
        return "number";
    }
}

测试类

public class ApiTest {

    @Test
    public void testCommand(){
        String tokenString = "4 3 2 - 1 + *";
        Stack<Expression> stack = new Stack<>();

        String[] tokenList = tokenString.split(" ");
        for (String s : tokenList) {
            if (isOperator(s)) {
                Expression rightExpression = stack.pop();
                Expression leftExpression = stack.pop();
                System.out.printf("popped from stack left: %s right: %s\n",
                        leftExpression.interpret(), rightExpression.interpret());
                Expression operator = getOperatorInstance(s, leftExpression, rightExpression);
                System.out.printf("operator: %s\n", operator);
                int result = operator.interpret();
                NumberExpression resultExpression = new NumberExpression(result);
                stack.push(resultExpression);
                System.out.printf("push result to stack: %s\n", resultExpression.interpret());
            } else {
                Expression i = new NumberExpression(s);
                stack.push(i);
                System.out.printf("push to stack: %s\n", i.interpret());
            }
        }
        System.out.printf("result: %s\n", stack.pop().interpret());
    }

    private static boolean isOperator(String s) {
        return s.equals("+") || s.equals("-") || s.equals("*");
    }

    private static Expression getOperatorInstance(String s, Expression left, Expression right) {
        switch (s) {
            case "+":
                return new PlusExpression(left, right);
            case "-":
                return new MinusExpression(left, right);
            case "*":
                return new MultiplyExpression(left, right);
            default:
                return new MultiplyExpression(left, right);
        }
    }
}

在这里插入图片描述

优缺点

优点
  1. 易于改变和扩展文法。因为该模式使用类表示文法,所以可以使用继承改变或扩展该文法。
  2. 每条文法规则都可以是一个类,所以可以很方便的实现一个简单的语言。
  3. 易于实现文法的定义。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂,还可以通过一些工具自动生成节点类代码。
  4. 增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合“开闭原则”。
缺点
  1. 对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。
  2. 执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。

适用场景

  1. 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
  2. 一些重复出现的问题可以用一种简单的语言来进行表达。
  3. 一个语言的文法较为简单。
  4. 执行效率不是关键问题。【注:高效的解释器通常不是通过直接解释抽象语法树来实现的,而是需要将它们转换成其他形式,使用解释器模式的执行效率并不高。】
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值