笨蛋学设计模式行为型模式-解释器模式【23】

行为型模式-解释器模式

8.10解释器模式

8.10.1概念

​ 解释器模式用于定义一个语言的文法规则,并解释和执行该语言中的表达式。它通过将语言表达式表示为一个抽象语法树,并提供解释器来遍历和执行该语法树,从而实现对语言的解释和执行

8.10.2场景

​ 例如编译器将源代码作为输入,通过词法分析和语法分析,构建抽象语法树(AST)。然后编译器使用解释器模式来遍历和执行这个抽象语法树,将源代码转换为机器码,并执行该机器码。

8.10.3优势 / 劣势

  • 抽象语法树使得算法独立于语法:抽象语法树作为构建解析器的结构,使得解析器的结构和实现与输入的文本语法无关
  • 易于单独扩展:由于解析器和语法规则分别通过抽象语法树和上下文无关文本来表示,因此易于单独扩展

  • 抽象语法树复杂:若语法规则非常复杂,抽象语法树可能会变得非常大和复杂,可能导致解析器实现和维护变得困难
  • 性能不如基于字符的解析器:由于抽象语法树和上下文无关文本都需要存储和解析

8.10.4解释器模式可分为

  • 抽象表达式AbstractExpression:定义了解释器的接口,包含了解释器的方法interpret
  • 终结符表达式TerminalExpressio:在语法中不能再分解为更小单元的符号
  • 非终结符表达式Non-terminalExpression:文法中的复杂表达式,它由终结符和其他非终结符组成
  • 上下文Context:包含解释器之外的一些全局信息,可以存储解释器中间结果,也可以用于向解释器传递信息

8.10.5解释器模式

java 复制代码
package com.technologystatck.designpattern.mode.interpreter;

public class Interpreters {
    public static void main(String[] args) {
        Context context = new Context();

        Expression expression = new AddException(new TerminalExpression(1), new TerminalExpression(2));

        int resulult = expression.interpret();
        System.out.println("Result:"+resulult);

        System.out.println("Context的全局变量:"+Context.CONTEXTSTR);

    }
}


//创建抽象表达式接口:定义解释器的接口,
//声明一个interpret方法,用于解释语言中的表达式
//抽象表达式接口
interface Expression {
    int interpret();
}

//创建具体的表达式类:实现抽象表达式接口,
//用于表示语言中的具体表达式
class TerminalExpression implements Expression{

    private int value;

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

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

//非终结符表达式:抽象表达式的一种,
//用于表示语言中的非终结符表达,通常包含其他表达式
class AddException implements Expression{

    private Expression left;
    private Expression right;

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

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

//上下文:包含解释器需要的一些全局信息或状态
class Context{
    //可以在上下文中存储一些全局信息或状态
    public static final String CONTEXTSTR="contextStr";
}

8.10.6实战

8.10.6.1题目描述

小明正在设计一个计算器,用于解释用户输入的简单数学表达式,每个表达式都是由整数、加法操作符+、乘法操作符组成的,表达式中的元素之间用空格分隔,请你使用解释器模式帮他实现这个系统。

8.10.6.2输入描述

每行包含一个数学表达式,表达式中包含整数、加法操作符(+)和乘法操作符(*)。 表达式中的元素之间用空格分隔。

8.10.6.3输出描述

对于每个输入的数学表达式,每行输出一个整数,表示对应表达式的计算结果。

8.10.6.4代码
java 复制代码
package com.technologystatck.designpattern.mode.interpreter;


import java.util.Scanner;
import java.util.Stack;

public class Test {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        Context context = new Context();

        //处理用户输入的数学表达式
        while (scanner.hasNextLine()) {
            String userInput = scanner.nextLine();
            Expression expression = parseExpression(userInput);

            if (expression != null) {
                //将表达式入栈
                context.pushExpression(expression);
                System.out.println(expression.interpret());
            } else {
                System.out.println("Invalid expression.");
            }
        }
    }

    //用于解析用户输入的数学表达式并返回相应的抽象表达式类
    private static Expression parseExpression(String userInput) {
        try {
            //定义一个表达式栈
            Stack<Expression> expressionStack = new Stack<>();
            //将用户输入的字符串转换为字符数组,方便处理。
            char[] tokens = userInput.toCharArray();
            //随意设置一个符号,
            //主要判断有没有识别进正确的+、*
            char operator='!';
            //遍历字符数组,处理字符串。
            for (int i = 0; i < tokens.length; i++) {
                char token = tokens[i];

                if (Character.isDigit(token)) {
                    //将数字传入进去
                    expressionStack.push(new NumberExpression(Character.getNumericValue(token)));
                    
                    //若栈中有两个以上的元素,说明可以进行运算
                    if (i + 1 <= tokens.length && expressionStack.size() >= 2) {
                        Expression right = expressionStack.pop();
                        Expression left = expressionStack.pop();

                        //若有匹配上的,则直接入栈
                        if (operator == '+') {
                            expressionStack.push(new AddException(left, right));
                        } else if (operator == '*') {
                            expressionStack.push(new MulException(left, right));
                        }else{
                            System.out.println("符号有误");
                        }
                        i++; //跳过下一个字符,因为已经处理过了
                    }
                }else{
                    //若不为数字,就进入这里,将符号赋给操作变量
                    operator=token;
                }
            }
            //返回最后一个元素,即最终的表达式。
            return expressionStack.pop();
        } catch (Exception e) {
            return null;
        }
    }
}


//创建抽象表达式接口
interface Expression {
    int interpret();
}

//创建终结符表达式类,实现抽象表达式接口
class NumberExpression implements Expression {
    private int number;

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

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

//创建非终结符表达式加法
class AddException implements Expression {

    private Expression left;
    private Expression right;

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

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

//创建非终结符表达式乘法
class MulException implements Expression {

    private Expression left;
    private Expression right;

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

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

//上下文类
class Context {
    //用于存储表达式
    private Stack<Expression> expressionStack = new Stack<>();

    //将给定的表达式入栈
    public void pushExpression(Expression expression) {
        expressionStack.push(expression);
    }

    //从栈顶弹出并返回一个表达式
    public Expression popExpression() {
        return expressionStack.pop();
    }

}

8.10.7总结

  • 优点:可以实现复杂的语法分析功能,同时解析器和语法规则易于单独扩展
  • 总结:就是通过自己定义独立的抽象语法树,使用抽象表达式用于定义解释器的接口、终结符表达式用于定义符号、非终结符表达式包含终结符和非终结符用于存储解释器结果或者向解释器传递信息、上下文来完成解释器的功能
  • 场景:需要解释和执行特定领域或业务规则的语言时
相关推荐
掘金-我是哪吒10 分钟前
分布式微服务系统架构第156集:JavaPlus技术文档平台日更-Java线程池使用指南
java·分布式·微服务·云原生·架构
亲爱的非洲野猪36 分钟前
Kafka消息积压的多维度解决方案:超越简单扩容的完整策略
java·分布式·中间件·kafka
wfsm38 分钟前
spring事件使用
java·后端·spring
微风粼粼1 小时前
程序员在线接单
java·jvm·后端·python·eclipse·tomcat·dubbo
缘来是庄1 小时前
设计模式之中介者模式
java·设计模式·中介者模式
rebel1 小时前
若依框架整合 CXF 实现 WebService 改造流程(后端)
java·后端
代码的余温3 小时前
5种高效解决Maven依赖冲突的方法
java·maven
慕y2743 小时前
Java学习第十六部分——JUnit框架
java·开发语言·学习
paishishaba3 小时前
Maven
java·maven
张人玉3 小时前
C# 常量与变量
java·算法·c#