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

发布于:2023-09-22 ⋅ 阅读:(65) ⋅ 点赞:(0)

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


前言

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

摘要

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

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

解释器模式

概念

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

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

结构

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

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

解释器模式结构图如图所示:

在这里插入图片描述

解释器模式的优缺点

优点:

  1. 解释器模式使得新的语法可以比较容易地添加进来,因为只需要添加对应的表达式即可。

  2. 解释器模式使得扩展语法非常方便,只需要添加对应的表达式即可。

缺点:

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

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

模式实现

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

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());
    }
}

执行结果如下:

在这里插入图片描述

测试用例代码解读

如上是一个Interpreter模式的测试类,用于测试Interpreter模式的实现。

main方法中,定义了两个输入字符串:input1input2

然后,定义了两个表达式:expression1expression2。其中,expression1是一个终端表达式,expression2是一个非终端表达式,它由两个终端表达式组成。

接着,创建了两个上下文对象:context1context2,它们分别以input1input2作为输入。

然后,分别调用expression1expression2interpret方法,将上下文对象作为参数传入,对输入字符串进行解释。最后,通过上下文对象的getOutput方法获取解释后的输出结果,并将其打印出来,用于验证Interpreter模式的实现是否正确。

小结

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

附录源码

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

总结

  解释器模式是一种行为型设计模式,用于解释一些特定的语法规则。在解释器模式中,我们需要定义抽象表达式、终结符表达式、非终结符表达式和上下文这几个核心角色。解释器模式可以用于解析自定义语言或编写自定义脚本。在解释器模式中,我们通常使用抽象语法树(AST)来表示句子。终结符表达式和非终结符表达式分别表示语法规则中的终结符和非终结符。终结符表达式表示语法规则中的基本元素,而非终结符表达式一般由多个终结符表达式和非

☀️建议/推荐你


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

📣关于我


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

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到