「聊设计模式」之解释器模式(Interpreter)

举报
bug菌 发表于 2023/09/26 10:32:23 2023/09/26
【摘要】 解释器模式是指定义一种语言文法,并设计该语言解释器来解释该语言中的句子。本质上来说,解释器模式属于一种特殊的`Visitor`设计模式。它在应用程序中通常用于解析自定义语言或者编写自定义脚本。在解释器模式中,通常会使用抽象语法树(`AST`)来表示句子。解释器模式中的几个核心角色包括抽象表达式(`AbstractExpression`)、终结符表达式(`TerminalExpression`)、非


🏆本文收录于《聊设计模式》专栏,专门攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎持续关注&&收藏&&订阅!


大家下午好,我是bug菌,今天我们继续聊设计模式。

前言

  设计模式是面向对象编程中的一种重要思想,能够帮助开发者更好地组织代码、提高代码可读性和可维护性。本文将介绍解释器模式(Interpreter),它是一种行为型设计模式,用于解释一些特定的语法规则。本文将使用Java语言作为示例来讲解解释器模式的实现。

摘要

  解释器模式是指定义一种语言文法,并设计该语言解释器来解释该语言中的句子。本质上来说,解释器模式属于一种特殊的Visitor设计模式。它在应用程序中通常用于解析自定义语言或者编写自定义脚本。在解释器模式中,通常会使用抽象语法树(AST)来表示句子。解释器模式中的几个核心角色包括抽象表达式(AbstractExpression)、终结符表达式(TerminalExpression)、非终结符表达式(NonterminalExpression)和上下文(Context)。

  在解释器模式中,抽象表达式是所有表达式的抽象基类,其中声明了各种解释方法。终结符表达式和非终结符表达式分别表示语法规则中的终结符和非终结符。终结符表达式表示语法规则中的基本元素,而非终结符表达式一般由多个终结符表达式和非终结符表达式组合而成。上下文则用来存储解释器执行过程中的中间结果,并提供给解释器访问。解释器通过解释语法规则,最终生成对应的结果。

解释器模式

概念

  解释器模式是一种行为型设计模式,它定义了一种语言文法的表示,并且定义了一个解释器,用于解释该语言中的句子。

  该模式的核心思想是:将一个复杂的语言(或表达式)解析成一个抽象语法树,然后再根据需要对该树进行遍历,并根据节点执行相应的操作。通过这种方式,可以将与语言相关的操作与语言本身分离开来,从而简化程序的设计和实现。

结构

解释器模式包含以下角色:

  1. 抽象表达式(Abstract Expression):定义解释器的接口,所有的表达式都必须实现该接口。
  2. 终结符表达式(Terminal Expression):表示语言中的基本元素,可以被解释。
  3. 非终结符表达式(Nonterminal Expression):表示语言中的复合元素,可以包含其他的表达式。
  4. 上下文(Context):包含解释器的全局信息,对解释器的调用和解释器之间的信息交换都通过上下文进行。
  5. 解释器(Interpreter):实现抽象表达式接口的具体类,用于解释语言中的语句或表达式。

如下是解释器模式的UML类图:

image.png

应用场景

解释器模式的应用场景如下:

  1. SQL解析器:SQL语句可以使用解释器模式来解析成不同的命令,以实现不同的功能。

  2. 数学公式解析器:对于给定的表达式,解析器将其解析成抽象语法树,并将其转换为可执行的代码。

  3. 编程语言解释器:可以将编程语言的语法规则转换成虚拟机指令序列,以供执行。

  4. 机器翻译:将一种语言的文本翻译成另一种语言的过程中,可以使用解释器模式。

  5. 正则表达式引擎:解析正则表达式的引擎可以使用解释器模式来实现。

  6. 模板解析器:可以将模板语言解析成可执行的代码。

  7. 配置文件解析器:对于配置文件中的各种配置项,解析器可以将其解析成可执行的代码以实现相应的配置。

  总之,解释器模式可以应用于任何需要将一种语言或规则转化为另一种语言或规则的场景。

解释器模式的优缺点

优点:

解释器模式的优点如下:

  1. 易于扩展新的语法规则,只需添加新的解释器即可。
  2. 可以对语言进行灵活的控制和调试,增加了代码的可读性和可维护性。
  3. 可以避免使用大量的条件判断语句,使代码更加简洁和易于理解。

缺点:

解释器模式的缺点如下:

  1. 大规模使用解释器模式可能会影响程序的性能,因为表达式需要被递归地解释。

  2. 在解释器模式中,我们需要考虑语法规则,并将其抽象为表达式,这可能会增加代码的复杂度,降低代码的可读性和可维护性。

  3. 解释器模式的性能通常不如编译器性能高,因为解释器需要通过运行时解释语言,而编译器可以在编译阶段优化代码。

  4. 如果语法规则过于复杂,可能需要大量的解释器来解释,导致代码量过大,可读性降低。

  5. 可能会出现环路依赖的问题,导致解释器无法进行解释。

模式实现

下面是解释器模式的具体实现。首先,我们定义抽象表达式和上下文:

package com.example.javaDesignPattern.interpreter;

/**
 * @Author bug菌
 * @Date 2023-09-19 22:15
 */
public abstract class AbstractExpression {
    public abstract void interpret(Context context);
}
package com.example.javaDesignPattern.interpreter;

/**
 * @Author bug菌
 * @Date 2023-09-19 22:16
 */
public class Context {
    private String input;
    private String output;

    public Context(String input) {
        this.input = input;
    }

    public String getInput() {
        return input;
    }

    public void setInput(String input) {
        this.input = input;
    }

    public String getOutput() {
        return output;
    }

    public void setOutput(String output) {
        this.output = output;
    }
}

  抽象表达式中定义了解释方法,需要子类来进行实现。上下文中存储了待解释的输入和解释结果。

  接下来,我们实现终结符表达式和非终结符表达式。本例中我们使用两个终结符表达式和一个非终结符表达式:

package com.example.javaDesignPattern.interpreter;

/**
 * @Author bug菌
 * @Date 2023-09-19 22:17
 */
public class TerminalExpression extends AbstractExpression {
    @Override
    public void interpret(Context context) {
        String input = context.getInput();
        context.setOutput(input.toLowerCase());
    }
}
package com.example.javaDesignPattern.interpreter;

/**
 * @Author bug菌
 * @Date 2023-09-19 22:17
 */
public class NonterminalExpression extends AbstractExpression {
    private AbstractExpression expression1;
    private AbstractExpression expression2;

    public NonterminalExpression(AbstractExpression expression1, AbstractExpression expression2) {
        this.expression1 = expression1;
        this.expression2 = expression2;
    }

    @Override
    public void interpret(Context context) {
        expression1.interpret(context);
        expression2.interpret(context);
    }
}

  在终结符表达式中,我们将输入字符串转换为小写,并将结果保存在上下文中。而在非终结符表达式中,我们将调用两个子表达式的解释方法来处理整个语法规则。

代码方法介绍

  1. AbstractExpression是抽象表达式基类,其中定义了解释方法,需要子类来进行实现。

  2. Context类用于存储待解释的输入和解释结果。

  3. TerminalExpression类是终结符表达式,用于将输入字符串转换为小写,并将结果保存在上下文中。

  4. NonterminalExpression类是非终结符表达式,用于调用两个子表达式的解释方法来处理整个语法规则。

  5. Main类用于测试我们的解释器模式实现。

测试用例

  最后,我们在Main方法中使用这些类来解释一些字符串:

package com.example.javaDesignPattern.interpreter;

/**
 * @Author bug菌
 * @Date 2023-09-19 22:18
 */
public class InterpreterTest {
    public static void main(String[] args) {
        String input1 = "HELLO WORLD";
        String input2 = "HELLO WORLD, I AM JAVA";

        AbstractExpression expression1 = new TerminalExpression();
        AbstractExpression expression2 = new NonterminalExpression(new TerminalExpression(), new TerminalExpression());

        Context context1 = new Context(input1);
        expression1.interpret(context1);
        System.out.println(context1.getOutput());

        Context context2 = new Context(input2);
        expression2.interpret(context2);
        System.out.println(context2.getOutput());
    }
}

执行结果如下:

image.png

测试用例代码解读

  如上是一个Interpreter模式的测试类,用于测试Interpreter模式的实现。在main方法中,定义了两个输入字符串:input1input2。然后,定义了两个表达式:expression1expression2。其中,expression1是一个终端表达式,expression2是一个非终端表达式,它由两个终端表达式组成。接着,创建了两个上下文对象:context1context2,它们分别以input1input2作为输入。然后,分别调用expression1expression2interpret方法,将上下文对象作为参数传入,对输入字符串进行解释。最后,通过上下文对象的getOutput方法获取解释后的输出结果,并将其打印出来,用于验证Interpreter模式的实现是否正确。

小结

  本文介绍了解释器模式,它是一种用于解释一些特定的语法规则的设计模式。解释器模式可以用于解析自定义语言或编写自定义脚本。

  为了实现解释器模式,我们需要定义抽象表达式、终结符表达式、非终结符表达式和上下文这几个核心角色。在解释器模式中,通常使用抽象语法树(AST)来表示句子。终结符表达式和非终结符表达式分别表示语法规则中的终结符和非终结符。终结符表达式表示语法规则中的基本元素,而非终结符表达式一般由多个终结符表达式和非终结符表达式组合而成。上下文用来存储解释器执行过程中的中间结果,并提供给解释器访问。解释器通过解释语法规则最终生成对应的结果。

附录源码

  如上涉及代码均已上传同步在GitHub,提供给同学们参考性学习。

总结

  本文介绍了解释器模式,它是一种用于解释一些特定的语法规则的设计模式。解释器模式可以用于解析自定义语言或编写自定义脚本。

  解释器模式的核心角色包括抽象表达式、终结符表达式、非终结符表达式和上下文。在解释器模式中,通常使用抽象语法树(AST)来表示句子。终结符表达式和非终结符表达式分别表示语法规则中的终结符和非终结符。上下文用来存储解释器执行过程中的中间结果,并提供给解释器访问。解释器通过解释语法规则最终生成对应的结果。

  解释器模式适用于需要将一种语言或规则转化为另一种语言或规则的场景,比如SQL解析器、数学公式解析器、编程语言解释器、机器翻译、正则表达式引擎、模板解析器、配置文件解析器等。但在实际应用中,需要注意解释器模式可能会影响程序的性能,以及语法规则的复杂度可能会降低代码的可读性和可维护性。

  总之,解释器模式是一种有用且强大的设计模式,可以帮助开发者更好地组织代码、提高代码可读性和可维护性。

☀️建议/推荐你

  如果想系统性的全面学习设计模式,建议小伙伴们直接毫无顾忌的关注这个专栏《聊设计模式》,无论你是想提升自己的编程技术,还是渴望更好地理解代码背后的设计思想,本专栏都会为你提供实用的知识和启发,帮助你更好地解决日常开发中的挑战,将代码变得更加优雅、灵活和可维护!

  最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。

  同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板、技术文章Markdown文档等海量资料。

📣关于我

  我是bug菌,CSDN | 掘金 | infoQ | 51CTO 等社区博客专家,历届博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12,华为云 | 阿里云| 腾讯云等社区优质创作者,全网粉丝合计15w+ ;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板等海量资料。


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。