Go设计模式--解释器模式

news2024/11/22 21:04:00

大家好,这里是每周都在陪你一起进步的网管~!今天继续学习设计模式—解释器模式

解释器模式是一种行为设计模式,可以用来在程序里创建针对一个特点领域语言的解释器,用于处理解释领域语言中的语句。换句话说,该模式定义了领域语言的抽象语法树以及用示来解释语法树的解释器。

模式使用场景

解释器模式,用于解决需要解释语言中的句子或表达式的问题。以下是一些可以在 程序中使用解释器模式的真实场景:

  • 处理配置文件

    • 许多应用程序使用配置文件来指定应用程序的行为方式。这些配置文件可以用 YAML 或 JSON 等 DSL 编写。解释器可用于解析这些配置文件并以应用编程语言对象的形式向应用程序提供配置信息。

  • 模板引擎

    • 模板引擎处理模板和一组变量以产生输出。模板是DSL的一个例子,可以使用Interpreter来解析和处理模板。

  • 数学表达式计算器

    • 数学表达式是我们日常都能接触到的,使用了一种特定领域语言语法书写语句或者叫表达式的实例

    • 这些表达式在程序里可以使用解释器模式进行解析和解释。例如,计算器应用程序可以使用解释器来解析和评估用户输入的数学表达式。

  • 自然语言处理

    • 在更高级的情况下,解释器模式可用于解析和解释自然语言,不过这通常会涉及想机器学习这样的更复杂的技术。

虽然解释器模式可以用来解决这些问题,但它并不总是最好的解决方案。对于复杂的语言,使用特定的解析库或工具或其他设计模式可能更有效。

下面我们先来学习一下解释器模式的结构组成,然后再尝试用代码自己实现一个解释器。

模式构成

解释器模式中的关键组件有:

  • 表达式接口:表示抽象语法树的元素并定义解释表达式的方法。

  • 具体表达式:实现表达式接口的结构,表示语言语法的各种规则或元素。

  • 上下文对象:用于保存解释过程中所需的任何必要信息或状态。

  • Parser 或 Builder:负责根据输入表达式构建抽象语法树的组件。

下面是解释器模式构成的UML类图:

1ecf30f09df844f503dc896012e5ef1a.png

看完解释器模式的结构组成后,我们接下来尝试应用解释器模式,用代码实现一个加法运算的解释器。

实现解释器模式

看了上面解释器的结构组成后我们结下来通过代码一步步实现其核心组件来演示怎么用代码实现解释器模式。

以下是如何在 Go 中实现解释器模式的步骤。

  1. 定义表示抽象语法树中元素的表达式接口。

  2. 创建实现 Expression 接口的具体表达式结构,例如 TerminalExpression 和 NonTerminalExpression。

  3. 定义一个上下文结构来保存解释过程中可能需要的任何必要数据或状态(这一步可选)。

  4. 创建解析器或构建器以根据输入表达式构造抽象语法树。 使用创建的抽象语法树和上下文解释表达式。

这里简单实现一个加减的运算器,我们对每种运算定义对应的Expression对象,在方法里实现具体的运算规则,避免所有的运算操作放到一个函数中,这体现了解释器模式的核心思想,将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。

我们先按照上面的步骤一,定义数学运算这一领域语言里表示抽象语法树中元素的表达式接口:

type Expression interface {
 Interpret() int
}

接下来创建Expression接口的具体实现类,在我们的加减法运算中需要实现操作数、加法、减法对应的实现类。

"本文使用的完整可运行源码
去公众号「网管叨bi叨」发送【设计模式】即可领取"
type NumberExpression struct {
 val int
}
// 解释--返回其整数值
func (n *NumberExpression) Interpret() int {
 return n.val
}

// 加法运算
type AdditionExpression struct {
 left, right Expression
}
// 解释--进行加法操作
func (n *AdditionExpression) Interpret() int {
 return n.left.Interpret() + n.right.Interpret()
}
// 减法运算
type SubtractionExpression struct {
 left, right Expression
}
// 解释--进行减法运算
func (n *SubtractionExpression) Interpret() int {
 return n.left.Interpret() - n.right.Interpret()
}

最后我们创建一个表达式解析器,它会根据输入表达式构造抽象语法树,使用创建的抽象语法树和上下文解释表达式。

"本文使用的完整可运行源码
去公众号「网管叨bi叨」发送【设计模式】即可领取"
type Parser struct {
 exp   []string
 index int
 prev  Expression
}

func (p *Parser) Parse(exp string) {
 p.exp = strings.Split(exp, " ")

 for {
  if p.index >= len(p.exp) {
   return
  }
  switch p.exp[p.index] {
  case "+":
   p.prev = p.newAdditionExpression()
  case "-":
   p.prev = p.newSubtractionExpression()
  default:
   p.prev = p.newNumberExpression()
  }
 }
}

func (p *Parser) newAdditionExpression() Expression {
 p.index++
 return &AdditionExpression{
  left:  p.prev,
  right: p.newNumberExpression(),
 }
}

func (p *Parser) newSubtractionExpression() Expression {
 p.index++
 return &SubtractionExpression{
  left:  p.prev,
  right: p.newNumberExpression(),
 }
}

func (p *Parser) newNumberExpression() Expression {
 v, _ := strconv.Atoi(p.exp[p.index])
 p.index++
 return &NumberExpression{
  val: v,
 }
}
// 返回Expression实例
// 调用Interpret方法会从右向左递归计算出公式结果
func (p *Parser) Result() Expression {
 return p.prev
}

最后,我们用使用 Parse 把客户端传递过来的加减法表达式解析成抽象语法树,然后运行解释器计算加减法表达式的结果。

"本文使用的完整可运行源码
去公众号「网管叨bi叨」发送【设计模式】即可领取"
func main() {
 p := &Parser{}
 p.Parse("1 + 3 + 3 + 3 - 3")
 res := p.Result().Interpret()
 expect := 7
 if res != expect {
  log.Fatalf("error: expect %d got %d", expect, res)
 }

 fmt.Printf("expect: %d, got: %d", expect, res)
}

本文的完整源码,已经同步收录到我整理的电子教程里啦,可向我的公众号「网管叨bi叨」发送关键字【设计模式】领取。

feefb15f3b03105666a33db649744966.png
公众号「网管叨bi叨」发送关键字【设计模式】领取。

总结

在程序中使用解释器模式的目标是: 定义特定于领域的语言及其语法,使用 AST(抽象语法树)表示语言中的表达式或句子,好让程序能够根据一组规则或操作解释或评估表达式

最后我们再来列举一下解释器模式的优缺点。 使用解释器模式的优点是:

  • 关注点分离:该模式将解释逻辑与数据表示分开。

  • 可扩展性:可以通过添加新的表达式结构轻松地扩展模式。

  • 可重用性:解释器模式可以在需要解析或解释特定领域语言的不同项目或上下文中重用。

使用解释器模式的缺点是:

  • 复杂性:随着语法规则数量的增加,模式会变得复杂。

  • 性能:对于大型表达式,抽象语法树的递归遍历可能很慢。

- END -

扫码关注公众号「网管叨bi叨」

给网管个星标,第一时间吸我的知识 👆

网管整理了一本《Go 开发参考书》收集了70多条开发实践。去公众号回复【gocookbook】领取!还有一本《k8s 入门实践》讲解了常用软件在K8s上的部署过程,公众号回复【k8s】即可领取!

觉得有用就点个在看  👇👇👇

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

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

相关文章

转转前端周刊第六十五期

转转前端周刊 本刊意在将整理业界精华文章给大家,期望大家一起打开视野 如果你有发现一些精华文章想和更多人分享,可以点击我们的公众号名称,将文章链接和你的解读文案发给我们!我们会对内容进行筛选和审核,保留你的推…

基于POM模式应用Playwright进行自动化测试

下方查看历史精选文章 重磅发布 - 自动化框架基础指南pdfv1.1大数据测试过程、策略及挑战 测试框架原理,构建成功的基石 在自动化测试工作之前,你应该知道的10条建议 在自动化测试中,重要的不是工具 前言 在现代软件开发中,自动化…

【Linux】Centos安装Redis数据库并内网穿透远程连接

文章目录 前言1. Linux(centos8)安装redis数据库2. 配置redis数据库3. 内网穿透3.1 安装cpolar内网穿透3.2 创建隧道映射本地端口 4. 配置固定TCP端口地址4.1 保留一个固定tcp地址4.2 配置固定TCP地址4.3 使用固定的tcp地址连接 前言 Redis作为一款高速缓存的key value键值对的…

springboot+jsp家政预约服务管理系统

智慧家政在线预约管理系统为用户提供家政服务预约服务,系统包括前台和后台。 前台用户可以先进行注册,填写个人的基本信息提交到服务器,服务器把数据保存到数据库。管理员对会员的信息进行验证后,会员通过验证后的用户名和密码进行…

vlc搭建rtsp直播Demo ffmpeg + nginx + flv.js实现rtsp网页播放Demo

文章目录 学习链接本地视频文件作为数据源推流步骤拉流步骤 本地摄像头作为数据源拉流步骤 vlc ffmpeg nginx flv.js 实现网页视频直播概括vlc打开摄像头,提供rtsp视频流nginx配置http-flv.conf的nginx配置文件 ffmpeg将rtsp视频流转为rtmp,推送给ngi…

硬文:为什么中本聪不给比特币设计最终性?

* * * 原创:刘教链 * * * 昨日刘教链公众号刊发原创文章《论比特币和以太坊的“宕机”》之后,引发了一些对区块链工作原理感兴趣的朋友的热烈讨论。其中,魔笛手社区群主Claire提了一个好问题:为什么中本聪不给比特币设计最终性&a…

Babylon.js实战WebXR/元宇宙

从 VR 音频实验到街机上的 VR 休闲游戏,再到使用 AR 或 VR 创建新协作方式的更严肃用途,阅读本文后,你应该对今天可以做什么有很好的了解。 事实上,在本文中,我将分享我一直致力于使用 Babylon.js 构建沉浸式或增强现…

Towards Open-Set Object Detection and Discovery(论文翻译)

Towards Open-Set Object Detection and Discovery 摘要 随着人类对知识的不断追求,开集目标检测(OSOD)被设计用于识别动态世界中的未知目标。然而,当前设置的一个问题是,所有预测的未知对象共享相同的类别为“未知”…

Kali-linux使用Metasploitable操作系统

Metasploitable是一款基于Ubuntu Linux的操作系统。该系统是一个虚拟机文件,从http://sourceforge.net/projects/metasploitable/files/Metasploitable2/网站下载解压之后可以直接使用,无需安装。由于基于Ubuntu,所以Metasploitable使用起来十…

卧龙、凤雏!两源码学得一,代码质量都不会差!

作者:小傅哥 博客:https://bugstack.cn 沉淀、分享、成长,让自己和他人都能有所收获!😄 有人问我,编程能力怎么提升,我说学源码学的。他有问我,是不学 Spring 源码比学 MyBatis 更好…

ESP32 event loop(事件循环)(17)

提示:本博客作为学习笔记,有错误的地方希望指正,主要参考乐鑫技术手册说明结合实例代码分析,结合理论知识学习后示例分析以及常见问题说明。 文章目录 一、ESP32 Event Loop 概述2、使用 esp_event APIs3、默认 event loop4、句柄…

Java进阶-面向对象进阶(抽象类接口内部类)

1 抽象类 1.1 概述 1.1.1 抽象类引入 ​ 父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有意义,而方法主体则没有存在的意义了(因为子类对象会调用自己重写的方法)。换句话…

Marvin - LLM驱动的AI函数开发包【Python】

Marvin是一个用于构建 AI 驱动软件的Python库。 Marvin 的工作是将 AI 直接集成到你的代码库中,使其看起来和感觉起来与任何其他功能一样。 Marvin 引入了一个新概念,称为 AI 函数(AI functions)。 AI函数与传统函数的不同之处在…

Java进阶-常用的API

1 Math类 1.1 概述 tips:了解内容 查看API文档,我们可以看到API文档中关于Math类的定义如下: Math类所在包为java.lang包,因此在使用的时候不需要进行导包。并且Math类被final修饰了,因此该类是不能被继承的。 Math类…

springboot+jsp超市库存进销存管理系统-a597o

Spring框架是Java平台的一个开放源代码的Full-stack(全栈)应用程序框架,和控制翻转容器的实现。Spring框架的一些核心功能理论,可以用于所有Java应用,Spring还为Java EE构建的Web应用提供大量的扩展支持。Spring框架没有实现任何的编程模型&a…

PHP生成带有雪花背景的验证码

<?session_start();?> <FORM METHOD=POST ACTION=""> <input type=text name=number maxlength=4><img src="YanZhengMa.php?act=init"> <INPUT TYPE="submit" name="sub"> </FORM><?//检…

【分布族谱】正态分布和二项分布的关系

文章目录 正态分布二项分布验证 正态分布 正态分布&#xff0c;最早由棣莫弗在二项分布的渐近公式中得到&#xff0c;而真正奠定其地位的&#xff0c;应是高斯对测量误差的研究&#xff0c;故而又称Gauss分布。测量是人类定量认识自然界的基础&#xff0c;测量误差的普遍性&am…

本地电脑做服务器搭建私人音乐网站ThinkMusic + cpolar内网穿透

文章目录 1. 前言2. 本地网页搭建2.1 环境使用2.2 支持组建选择 3. 网页安装3. 本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 转发自CSDN lisacpolar的文章&#xff1a;ThinkMusic源码搭建音乐网站&#xff0c;并实现公网访问 1. 前言 在我们的日…

数据结构初阶(1)(一些学习数据结构所需掌握的先导知识:包装类、装箱与拆箱、泛型【泛型的编译——擦除机制、泛型的上界、泛型方法、裸类型】、List简介)

目录 包装类 基本数据类型&#xff08;Primitive Types&#xff09;&#xff1a; 包装类&#xff08;Wrapper Classes&#xff09;&#xff1a; 装箱和拆箱 自动装箱和自动拆箱 泛型 泛型的编译——很重要的擦除机制&#xff1a; 泛型的上界&#xff1a; 泛型方法&…

LC-1335. 工作计划的最低难度(区间DP、记忆化搜索==>动态规划)

1335. 工作计划的最低难度 难度困难97 你需要制定一份 d 天的工作计划表。工作之间存在依赖&#xff0c;要想执行第 i 项工作&#xff0c;你必须完成全部 j 项工作&#xff08; 0 < j < i&#xff09;。 你每天 至少 需要完成一项任务。工作计划的总难度是这 d 天每一…