18、设计模式之解释器模式(Interpreter)

一、什么是解释器模式

解释器模式是一种行为型设计模式。解释器模式就像是一种自定义语言,我们可以定义该语言的语法规则,然后从中解析出具体的命令或表达式,最终执行相应的操作。

eg:这种模式比较冷门,不怎么使用。

二、角色组成

抽象表达式(Abstract Expression):定义了解释器需要实现的接口。

终止符表达式(Terminal Expression):表示该语言中的终止符(例如,变量、函数等),终止符表达式的解释方法通常很简单,往往只包含一两行代码。

非终止符表达式(Nonterminal Expression):表示该语言中的非终止符(例如,语句块、条件语句、循环语句等),非终止符表达式的解释方法通常需要对内部表达式进行递归调用。

上下文(Context):存储了当前语言解释器的状态信息。

三、优缺点

优点:

扩展性好。由于解释器模式定义了语言的文法,因此可以很容易地添加新的表达式类和解释方法,从而扩展语言的解释能力。

灵活性高。由于解释器模式是基于接口设计的,因此可以很容易地替换解释器的实现或更改其执行方式,从而满足不同的需求。

缺点:

由于解释器模式需要定义很多类和解释方法,因此代码量比较大,实现起来有一定的复杂。度。

对于复杂的表达式,解释器模式的解释器可能需要占用更多的内存空间和运行资源,从而导致程序性能下降。

如果语言的文法非常复杂,解释器模式的实现可能会很困难,而且难以维护和扩展。

四、应用场景

4.1 生活场景

数学表达式求值:我们可以设计一个数学表达式解释器,将数学表达式解释为可计算的结果,从而方便进行运算。

游戏AI:通过解释器来解析AI所需的数据和指令,然后根据这些数据和指令执行相应的行为。

语言翻译:将一种语言的文本解析为另一种语言的文本。翻译器可以将输入的文本解析为一系列的单词、短语和句子,然后通过对这些语言结构进行翻译,将其转换成目标语言的文本。

4.2 java场景

正则表达式。正则表达式是一种用于描述字符串模式的语言,它可以非常灵活地描述满足特定模式的字符串,因此解释器模式是实现正则表达式的常用方法之一。

SQL解析器。SQL是一种结构化查询语言,用于在关系型数据库中进行数据操作和管理。为了将SQL语句转换为数据库操作,我们需要实现一个SQL解析器,将SQL语句解释为可执行的SQL命令,这就需要使用解释器模式。

java代码:需要编译器进行编译后才能运行,这个编译器就相当于解释器。

五、代码实现

下面以四则运算表达式,来解释一下解释器模式。

抽象表达式:Expression

终止符表达式:Constant、Variable

非终止符表达式:AddExpression、SubExpression

上下文:InterpreterVariables

5.0 UML类图

java 复制代码
5.1 Expression------抽象表达式(Abstract Expression)
/**
 *
 * 1.抽象表达式(Abstract Expression)
 * 抽象表达式定义了用于解释特定语言的接口。
 */
public abstract class Expression {
    //定义解释器方法
    public abstract int interpret();
}
5.2 终结符表达式(Terminal Expression)
/**
 * 
 * 2.终结符表达式(Terminal Expression):变量
 * 终结符表达式表示语言中的终止符(例如,变量、关键字等)。
 */
public class Variable extends Expression{
    private String name;
 
    public Variable(String name) {
        this.name = name;
    }
 
    @Override
    public int interpret() {
        // 从上下文中获取该变量对应的值
        return InterpreterVariables.getValue(this.name);
    }
}
/**
 * 
 * 2.终结符表达式(Terminal Expression):常量
 * 终结符表达式表示语言中的终止符(例如,变量、关键字等)。
 */
public class Constant extends Expression{
    private int value;
 
    public Constant(int value) {
        this.value = value;
    }
 
    @Override
    public int interpret() {
        // 直接返回常量值
        return value;
    }
}
5.3 非终结符表达式(Nonterminal Expression)
/**
 * 
 * 3.非终结符表达式(Nonterminal Expression):加法运算
 * 非终结符表达式表示语言中的非终止符(例如,语句块、语句等)。
 */
public class AddExpression extends Expression{
    // 左操作数
    private Expression left;
    // 右操作数
    private Expression right;
 
    public AddExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }
 
    @Override
    public int interpret() {
        // 对左右操作数分别进行解释器求值,然后计算结果
        return left.interpret() + right.interpret();
    }
}
/**
 * 
 * 3.非终结符表达式(Nonterminal Expression):减法运算
 * 非终结符表达式表示语言中的非终止符(例如,语句块、语句等)。
 */
public class SubExpression extends Expression{
    private Expression left;
    private Expression right;
 
    public SubExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }
 
    @Override
    public int interpret() {
        return left.interpret() - right.interpret();
    }
}
5.4 InterpreterVariables------上下文(Context)
/**
 * 
 * 4.上下文(Context):负责存储变量名和其值之间的映射关系
 * 上下文保存了解释器解释表达式需要的信息。
 */
public class InterpreterVariables {
    private static Map<String, Integer> variables = new HashMap<>();
 
    // 根据变量名获取其对应的值
    public static int getValue(String name) {
        if(variables.containsKey(name)) {
            return variables.get(name);
        }
        // 默认返回0
        return 0;
    }
 
    // 设置变量名和其对应的值
    public static void setValue(String name, int value) {
        variables.put(name, value);
    }
}
5.5 testInterpreter
/**
 * 
 * 解释器模式测试类
 */
@SpringBootTest
public class TestInterpreter {
    @Test
    void testInterpreter(){
        // 创建变量x、y和常量1,并设置变量x和y的值
        Variable x = new Variable("x");
        Variable y = new Variable("y");
        Constant c = new Constant(1);
 
        InterpreterVariables.setValue("x", 10);
        InterpreterVariables.setValue("y", 5);
 
        // 创建解释器表达式(x - y)+ 1
        Expression expression = new AddExpression(new SubExpression(x, y), c);
 
        // 解释表达式,并获取最终结果
        int result = expression.interpret();
        System.out.println("计算结果:" + result);
    }
}

六、总结

解释器是一个简单的语法分析工具,每个语法都需要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来非常多的麻烦。因此,尽量不要在重要的模块中使用解释器模式,在项目中可以使用shell、JRuby、Groovy等脚本语言来代替解释器模式,也可以用开源包,比如Express4J、JEP,功能都很强大。

出现以下场景,可以考虑使用(尽量别用,大佬除外):

需要定义一种自定义语言,并对其进行解析和执行。

需要实现一种自定义的文件格式,并对其进行解析和处理。

需要对复杂的数据结构进行解析,并对其进行处理或转换。

需要实现一些自定义的算法或规则,并对其进行解析和执行。

相关推荐
lunz_fly19923 小时前
Oracle清理:如何安全删除trace, alert和archivelog文件?
oracle
晨米酱5 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机10 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机11 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机11 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机11 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤11 小时前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机1 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴1 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript