行为型设计模式10-解释器模式

news2024/11/16 3:43:53

🧑‍💻作者:猫十二懿

❤️‍🔥账号:CSDN 、掘金 、个人博客 、Github

🎉公众号:猫十二懿

解释器模式

1、解释器模式介绍

解释器模式(Interpreter Pattern)是一种行为设计模式,它定义了一个语言的语法表示,并且使用解释器来解释这个语法

该模式的核心思想是将一个语言表达式表示为一个抽象语法树,然后定义解释器来遍历这棵语法树并执行相应的操作。解释器模式常用于处理特定的语法或规则,并且可以根据需求进行灵活的扩展。

1.1 解释器模式基本实现

如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这 样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

解释器模式结构图:

image-20230524192149721

以下是解释器模式的关键角色:

  1. 抽象表达式(Abstract Expression):定义了解释器的接口,其中包含了一个抽象的interpret()方法,所有具体表达式都必须实现这个接口。
  2. 终结符表达式(Terminal Expression):实现了抽象表达式接口,并表示语法中的终结符(即不可再分的最小单元),例如变量或常量。
  3. 非终结符表达式(Non-terminal Expression):实现了抽象表达式接口,并表示语法中的非终结符,即可以由其他表达式组合而成的复杂表达式
  4. 上下文(Context):包含解释器解释的上下文信息,一般是一个包含了各种全局信息的数据结构。

解释器模式的工作流程如下:

  1. 客户端创建并配置解释器的上下文。
  2. 客户端创建具体的解释器对象,并用抽象表达式接口进行引用。
  3. 客户端将表达式构建成抽象语法树。
  4. 客户端调用解释器的interpret()方法,传入上下文对象,执行解释操作。

AbstractExpression(抽象表达式),声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。

/**
 * @author Shier
 * CreateTime 2023/5/24 19:27
 * 抽象表达式
 */
public abstract class AbstractExpression {
    /**
     * 解释操作 - 类似翻译
     * @param context
     */
    public abstract void interpret(Context context);
}

TerminalExpression(终结符表达式),实现与文法中的终结符相关联 的解释操作。实现抽象表达式中所要求的接口,主要是一个interpret()方 法。文法中每一个终结符都有一个具体终结表达式与之相对应。

/**
 * @author Shier
 * CreateTime 2023/5/24 19:29
 * 终结者表达式
 */
public class TerminalExpression extends AbstractExpression{
    @Override
    public void interpret(Context context) {
        System.out.println("终端解释器");
    }
}

NonterminalExpression(非终结符表达式),为文法中的非终结符实现 解释操作。对文法中每一条规则R1、R2、…、Rn都需要一个具体的非终结符 表达式类。通过实现抽象表达式的interpret()方法实现解释操作。解释操 作以递归方式调用上面所提到的代表R1、R2、…、Rn中各个符号的实例变量。

/**
 * @author Shier
 * CreateTime 2023/5/24 19:29
 * 非终结者表达式
 */
public class NotTerminalExpression extends AbstractExpression{
    @Override
    public void interpret(Context context) {
        System.out.println("非终端解释器");
    }
}

Context,包含解释器之外的一些全局信息。

/**
 * @author Shier
 * CreateTime 2023/5/24 19:31
 * 解释器外的全局信息
 */
public class Context {
    private String input;
    private String outPut;

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

客户端代码,构建表示该文法定义的语言中一个特定的句子的抽象语法树。调用解释操作。

/**
 * @author Shier
 * CreateTime 2023/5/24 19:31
 */
public class ExpressClient {
    public static void main(String[] args) {
        Context context = new Context();
        ArrayList<AbstractExpression> arrayList = new ArrayList<>();
        arrayList.add(new TerminalExpression());
        arrayList.add(new NotTerminalExpression());
        arrayList.add(new TerminalExpression());
        arrayList.add(new TerminalExpression());
        for (AbstractExpression expression : arrayList) {
            expression.interpret(context);
        }
    }
}

最终得出的结果:

image-20230524193540380

到此,应该大概知道了解释器模式的使用过程了,下面再来看看具体的例子怎么讲述这个解释器模式吧

2、具体例子实现解释器模式

假设您是一位旅行者,正在国外旅游。在您旅行的过程中,您需要与当地人交流,但您并不懂得当地的语言。您可以雇佣一名翻译来帮助您与当地人沟通。

2.1 不使用解释器模式进行翻译

/**
 * @author Shier
 * CreateTime 2023/5/24 19:50
 */
public class ExpreMain {
    public static void main(String[] args) {
        String sentence = "How much does it cost?";
        String language = "中文";

        String translatedSentence;

        if (language.equals("English")) {
            translatedSentence = translateToEnglish(sentence);
        } else if (language.equals("Spanish")) {
            translatedSentence = translateToSpanish(sentence);
        } else if (language.equals("French")) {
            translatedSentence = translateToFrench(sentence);
        } else if (language.equals("中文")) {
            translatedSentence = translateToChinese(sentence);
        } else {
            translatedSentence = "Language not supported";
        }

        System.out.println("Translated sentence: " + translatedSentence);
    }

    private static String translateToEnglish(String sentence) {
        // 实现将句子翻译为英语的逻辑
        return sentence;
    }

    private static String translateToChinese(String sentence) {
        // 实现将句子翻译为英语的逻辑
        return "要多少钱?";
    }

    private static String translateToSpanish(String sentence) {
        // 实现将句子翻译为西班牙语的逻辑
        return "¿Cuánto cuesta?";
    }

    private static String translateToFrench(String sentence) {
        // 实现将句子翻译为法语的逻辑
        return "Combien ça coûte ?";
    }
}

翻译得到的结果:

image-20230524195437092

在这个例子中,我们使用简单的条件语句来根据当前语言选择相应的翻译方法。根据当前语言环境,我们调用对应的翻译方法来翻译句子。最后,我们输出翻译后的句子。

虽然这种方法可以实现语言翻译的功能,但它存在一些问题:

  1. 增加新的语言需要修改代码:如果需要添加新的语言支持,我们需要修改主程序的条件语句,并添加一个新的翻译方法。这违反了开闭原则,增加了代码的耦合性。
  2. 代码可读性差:随着语言支持的增加,条件语句会变得越来越复杂,代码可读性和维护性下降。
  3. 缺乏灵活性和扩展性:在解释器模式中,我们可以轻松地添加新的语法规则或扩展现有的规则,而不会对现有代码产生太大的影响。但是,使用条件语句的方法不够灵活,添加新的语言或语法规则需要修改大量的代码。

总结起来,虽然在简单的场景下,使用条件语句可能是一种简单直接的解决方案,但是随着需求的变化和复杂性的增加,这种方法会导致代码的维护困难、扩展性差以及违反设计原则。而解释器模式能够更好地管理和扩展语法规则,提供了更灵活、可扩展的解决方案。

2.2 使用解释器模式

具体的实现过程:

抽象表达类:

/**
 * @author Shier
 */
public interface Expression {
    String interpret(Context context);
}

语言表达符号:

/**
 * @author Shier
 */
public class LanguageExpression implements Expression {
    private String language;

    public LanguageExpression(String language) {
        this.language = language;
    }

    @Override
    public String interpret(Context context) {
        return context.translate(language);
    }
}

非语言表达符:

/**
 * @author Shier
 */
public class NonLanguageExpression implements Expression {
    private Expression firstExpression;
    private Expression secondExpression;

    public NonLanguageExpression(Expression firstExpression, Expression secondExpression) {
        this.firstExpression = firstExpression;
        this.secondExpression = secondExpression;
    }

    @Override
    public String interpret(Context context) {
        String firstTranslation = firstExpression.interpret(context);
        String secondTranslation = secondExpression.interpret(context);
        return firstTranslation + " " + secondTranslation;
    }
}

Context:

import java.util.HashMap;
import java.util.Map;

public class Context {
    private Map<String, String> translations;

    public Context() {
        translations = new HashMap<>();
    }

    public void addTranslation(String language, String translation) {
        translations.put(language, translation);
    }

    public String translate(String language) {
        return translations.get(language);
    }
}

客户端:

public class InterpreterExample {
    public static void main(String[] args) {
        Context context = new Context();
        context.addTranslation("English", "Hello");
        context.addTranslation("French", "Bonjour");
        context.addTranslation("Spanish", "Hola");

        Expression englishExpression = new LanguageExpression("English");
        Expression frenchExpression = new LanguageExpression("French");
        Expression spanishExpression = new LanguageExpression("Spanish");

        Expression combinedExpression = new NonLanguageExpression(frenchExpression,spanishExpression);

        String travelerLanguage = "French";

        String translatedText = null;
        if (travelerLanguage.equals("English")) {
            translatedText = englishExpression.interpret(context);
        } else if (travelerLanguage.equals("French")) {
            translatedText = combinedExpression.interpret(context);
        } else if (travelerLanguage.equals("Spanish")) {
            translatedText = spanishExpression.interpret(context);
        }

        System.out.println("Traveler: " + translatedText);
    }
}

结果:

image-20230524203914218

虽然上面的翻译例子,可以使用适配器模式进行,但是和解释器模式实现起来的过程还是不一样的,但都能达到相同的目的。

适配器模式和解释器模式的区别:

  1. 解释器模式: 解释器模式用于解析和解释特定类型的语言或表达式。它通常用于构建编程语言解释器或规则引擎。该模式的核心思想是将语言或表达式表示为一个抽象语法树,并定义一组解释器来执行相应的操作。

解释器模式的关键组成部分包括以下几个角色:

  • 抽象表达式(Abstract Expression):定义了解释器的接口,包含解释方法interpret()。
  • 终结表达式(Terminal Expression):表示语法中的终结符,执行具体解释操作。
  • 非终结表达式(Non-terminal Expression):表示语法中的非终结符,通常由多个终结符和/或非终结符组合而成。

解释器模式的主要目的是通过定义一组解释器将语言或表达式转换为可执行的操作,以实现特定的功能。

  1. 适配器模式: 适配器模式用于将一个类的接口转换为另一个类的接口,使得原本由于接口不兼容而无法工作的类能够一起工作。适配器模式允许已存在的类与其他类协同工作,而无需修改其源代码。

适配器模式的核心思想是引入一个适配器类,该类实现了目标接口,并在内部包含了要适配的类的实例。适配器类通过调用包含的实例的方法,将对目标接口的调用转发给适配的类。

适配器模式的主要目的是解决不同接口之间的兼容性问题,使得不兼容的类能够协同工作。

总结: 解释器模式和适配器模式的区别在于它们解决的问题领域和应用场景不同。解释器模式主要用于解析和执行特定类型的语言或表达式,而适配器模式主要用于将不兼容的接口进行适配,使得不兼容的类能够协同工作。

2.3 使用解释器模式进行不同的日期格式转换

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

// 抽象表达式接口
interface Expression {
    LocalDate interpret(String context);
}

// 具体表达式类 - 解析“yyyy-MM-dd”格式的日期
class YearMonthDayExpression implements Expression {
    @Override
    public LocalDate interpret(String context) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        return LocalDate.parse(context, formatter);
    }
}

// 具体表达式类 - 解析“dd/MM/yyyy”格式的日期
class DayMonthYearExpression implements Expression {
    @Override
    public LocalDate interpret(String context) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
        return LocalDate.parse(context, formatter);
    }
}

// 上下文类
class Context {
    private Expression expression;

    public Context(Expression expression) {
        this.expression = expression;
    }

    public LocalDate interpret(String context) {
        return expression.interpret(context);
    }
}

// 示例
public class Main {
    public static void main(String[] args) {
        // 输入日期字符串
        String dateString = "2023-05-24";

        // 创建解析器
        Expression expression = new YearMonthDayExpression();
        Context context = new Context(expression);

        // 解析日期
        LocalDate date = context.interpret(dateString);

        // 打印解析结果
        System.out.println("Parsed date: " + date);
    }
}

在上面的示例中,我们首先定义了一个抽象表达式接口Expression,其中包含一个interpret方法用于解释日期字符串。然后,我们实现了两个具体的表达式类YearMonthDayExpressionDayMonthYearExpression,分别用于解析不同格式的日期字符串。

我们还创建了一个上下文类Context,它接收一个表达式作为参数,并通过调用表达式的interpret方法来解析日期字符串。

在示例的main方法中,我们定义了一个日期字符串dateString,然后创建了一个YearMonthDayExpression实例作为解析器,并将其传递给上下文对象。最后,我们使用上下文对象解析日期字符串,并打印解析结果。

这只是一个简单的示例,你可以根据实际需求进行扩展和修改。通过使用解释器模式,你可以根据不同的日期格式创建不同的解析器,并使用统一的接口进行日期解析。

到此应该知道解释器模式的用处,我们再来总结一下解释器模式

3、解释器模式总结

3.1 使用场景

  1. 构建编程语言解释器:解释器模式非常适合构建编程语言解释器,可以将语言的语法规则定义为解释器中的表达式类,并执行对应的操作。
  2. 规则引擎:解释器模式可以用于实现规则引擎,通过解释器来解析和执行规则,对输入数据进行处理和判断。
  3. 数学表达式解析:解释器模式可用于解析和计算数学表达式,将表达式表示为抽象语法树,并通过解释器执行相应的操作。
  4. 查询语言解析:解释器模式可以用于解析和执行查询语言,例如数据库查询语言等。

3.2 解释器模式优点

  1. 扩展性好:通过增加新的表达式类,可以灵活地扩展解释器的功能。
  2. 易于实现语法:解释器模式可以通过解析语法树来定义语言的语法规则,使得语法定义清晰明确。
  3. 易于实现特定领域语言:可以使用解释器模式来实现特定领域语言(Domain-Specific Language, DSL),以满足特定问题领域的需求。

3.3 解释器模式缺点

  1. 执行效率相对较低:解释器模式通常需要解析和执行语法树,可能会导致较低的执行效率。
  2. 可能引起类膨胀:如果语法规则非常复杂,可能会导致需要大量的表达式类,从而增加了类的数量,使代码复杂度增加。

实现规则引擎,通过解释器来解析和执行规则,对输入数据进行处理和判断。
3. 数学表达式解析:解释器模式可用于解析和计算数学表达式,将表达式表示为抽象语法树,并通过解释器执行相应的操作。
4. 查询语言解析:解释器模式可以用于解析和执行查询语言,例如数据库查询语言等。

3.2 解释器模式优点

  1. 扩展性好:通过增加新的表达式类,可以灵活地扩展解释器的功能。
  2. 易于实现语法:解释器模式可以通过解析语法树来定义语言的语法规则,使得语法定义清晰明确。
  3. 易于实现特定领域语言:可以使用解释器模式来实现特定领域语言(Domain-Specific Language, DSL),以满足特定问题领域的需求。

3.3 解释器模式缺点

  1. 执行效率相对较低:解释器模式通常需要解析和执行语法树,可能会导致较低的执行效率。
  2. 可能引起类膨胀:如果语法规则非常复杂,可能会导致需要大量的表达式类,从而增加了类的数量,使代码复杂度增加。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/677133.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kafka系列之:对源连接器的的Exactly-Once支持

Kafka系列之&#xff1a;对源连接器的的Exactly-Once支持 一、背景二、目标三、公共接口四、连接器 API 扩展五、REST API验证六、新指标七、计划变更八、任务计数记录九、重新平衡的准备十、源任务启动十一、领导者访问配置主题十二、用于隔离事务生产者的管理 API十三、解决了…

论文阅读 - SegFormer

文章目录 1 概述2 模型说明2.1 总体结构2.2 Hierarchical Transformer Encoder2.3 Lightweight All-MLP Decoder 3 SegFormer和SETR的比较参考资料 1 概述 图像分割任务和图像分类任务是非常相关的&#xff0c;前者是像素级别的分类&#xff0c;后者是图像级别的分类。基于分类…

不到3000块,搭建IT人的实验平台!性能可媲美服务器!

作为IT从业者&#xff0c;特别是运维这个岗位&#xff0c;没有自己的实验平台真的特别难受&#xff0c;那么如何搭建自己的实验平台呢&#xff1f;这是我最近思考并付诸实践的一个事情&#xff0c;最终找到了自己觉得比较可以的方案。 01 我的需求是什么&#xff1f; 大内存容量…

TypeScript——类(class)

ES6 中类的用法 下面我们先回顾一下 ES6 中类的用法&#xff0c;更详细的介绍可以参考 ECMAScript 6 入门 - Class 属性和方法 使用 class 定义类&#xff0c;使用 constructor 定义构造函数。 通过new生成新实例的时候&#xff0c;会自动调用构造函数。 class Person{con…

leetcode877. 石子游戏(动态规划-java)

石子游戏 leetcode877. 石子游戏题目描述暴力递归代码演示 动态规划 动态规划专题: leetcode877. 石子游戏 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;https://leetcode.cn/problems/stone-game 题目描述 Alice 和 Bob 用几堆石子在做游戏。一共有…

HTTP与Fiddler使用

HTTP与Fiddler使用 HTTP与Fiddler使用FidderHTTP的报文结构&#xff1a;其他请求头User-agentReferer和OrigincookieHTTP状态码 HTTP与Fiddler使用 HTTP协议是使用十分广泛的应用层协议&#xff0c;也是一个可以由程序员进行设置的一个协议。该协议的结构规定了浏览器与万维网…

【C++】通讯录的基本实现,附有源码分享

目录 1、运行环境 2、系统实现功能 2.1菜单功能 2.2退出通讯录功能 2.3添加联系人功能 2.4显示联系人功能 2.5删除联系人功能 2.6查找联系人功能 2.7修改联系人功能 2.8清空联系人功能 2.9动态扩容功能 2.10选择优化功能 2.11文件操作 3、源码分享 1、运行环境 …

【备战秋招】每日一题:2023.04.26-华为OD机式-第三题-MC方块

在线评测链接:P1231 题目内容 MC最新版本更新了一种特殊的方块&#xff0c;幽匿催发体。这种方块能够吸收生物死亡掉落的经验并感染周围方块&#xff0c;使其变成幽匿块。Steve想要以此为基础尝试搭建一个经验仓库&#xff0c;他来到了创造超平坦模式&#xff0c;在只有草方块…

【微信小程序开发】第 7 课 - 小程序的常用组件

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、小程序中组件的分类 3、常用的视图容器类组件 3.1、view 组件 3.2、scroll - view 组件 3.3、swiper 和 swiper…

blfs:为lfs虚拟机增加桌面02

参考书籍&#xff1a; BLFS11.3 LFS11.3&#xff08;这里面有软件安装的详细说明&#xff09; 树莓派Linux操作系统移植&#xff08;这里面有桌面系统的脉络梳理&#xff09; 参考视频 https://www.youtube.com/watch?vcavxyXBgJ6Q&listPLyc5xVO2uDsBK_3VZOek8ICsxewOO4DU…

Vue3 网络请求——axios 高级用法之 axios 拦截器实战与并发请求

文章目录 &#x1f4cb;前言&#x1f3af;关于拦截器&#x1f3af;项目创建&#x1f3af;代码分析&#x1f3af;补充&#xff1a;并发请求&#x1f9e9;axios.all() 和 Promise.all() 的区别 &#x1f4dd;最后 &#x1f4cb;前言 Axios 是一个流行的基于 Promise 的 HTTP 客户…

机器学习中的多分类问题

文章标题&#xff1a;机器学习中的多分类问题 机器学习中的分类问题可以大致分为二分类和多分类两种。在二分类问题中&#xff0c;模型需要将输入数据分为两类&#xff1b;而在多分类问题中&#xff0c;模型需要将输入数据分为多个类别。本文将介绍机器学习中的多分类问题及其…

C语言指针类型,8个例子给你讲明白

0.问题 知乎上回答了一个粉丝问题&#xff0c; 结果这兄弟又连续问了几个问题&#xff1a; 好吧&#xff0c;帮人帮到底&#xff0c;送佛送到西&#xff01;给你讲彻底点吧&#xff01; 1. int va; 这是一个整型变量&#xff0c;32位CPU的话&#xff0c;占有32个bite 2. in…

Redis入门(1)

1.NOSQL概述 1.1.什么是NOSQL NoSQL&#xff0c;泛指非关系型的数据库。随着互联网web2.0网站的兴起&#xff0c;传统的关系数据库在处理web2.0网站&#xff0c;特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心&#xff0c;出现了很多难以克服的问题&…

设计模式之享元模式笔记

设计模式之享元模式笔记 说明Flyweight(享元)目录享元模式示例类图抽象图形类I图形类L图形类O图形类工厂类测试类 说明 记录下学习设计模式-享元模式的写法。JDK使用版本为1.8版本。 Flyweight(享元) 意图:运用共享技术有效地支持大量细粒度的对象。 结构: 其中&#xff1…

MCU(Cortex - M3/M4)启动加载过程和内存分配原理 笔记

最近发现对基础不太熟悉&#xff0c;写篇笔记记录一下MCU启动到用户C语言运行&#xff0c;之前做了那些工作&#xff0c;同时flash和Ram又分别保存了那个数据&#xff0c;每一段又是什么意义&#xff0c;方便后续自己忘记了&#xff0c;查阅。 一、 MCU启动 在MCU上电/复位之后…

WireShark常用协议抓包与原理分析

1.ARP协议(地址解析协议) nmap 发现网关nmap -sn 192.168.133.2wireshark 抓请求包和响应包 arp请求包内容 arp响应包内容 总结:请求包包含包类型(request),源IP地址,源MAC地址,目标IP地址,目标MAC地址(未知,此处为全0);响应包包含包类型(reply),源IP地址,源…

DAY28:回溯算法(三)组合总和Ⅲ+电话号码字母组合

文章目录 216.组合总和Ⅲ思路树形结构 完整版debug测试逻辑错误&#xff1a;没有输出 剪枝操作剪枝版本continue的用法剪枝最后是continue还是return的讨论 17.电话号码的字母组合思路树形结构 伪代码字符串中的字符2转化成int的方法字符串字符与int转换补充字符串与字符 完整版…

CTF-Show密码学【摩斯码、培根密码】

萌新 密码33 一、题目信息 题目名称&#xff1a;我想吃培根题目描述&#xff1a;-- — .-. … . …–.- … … …–.- -.-. — — .-… …–.- -… …- - …–.- -… .- -.-. — -. …–.- … … …–.- -.-. — — .-… . .-. …–.- – – -… -… – -… – -… – – – -…

ASP.NET Core MVC 从入门到精通之缓存

随着技术的发展&#xff0c;ASP.NET Core MVC也推出了好长时间&#xff0c;经过不断的版本更新迭代&#xff0c;已经越来越完善&#xff0c;本系列文章主要讲解ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容&#xff0c;适用于初学者&#xff0c;在校毕业生&#xff0c…