设计模式之解释器模式

阅读建议

嗨,伙计!刷到这篇文章咱们就是有缘人,在阅读这篇文章前我有一些建议:

  1. 本篇文章大概5000多字,预计阅读时间长需要5分钟。
  2. 本篇文章的实战性、理论性较强,是一篇质量分数较高的技术干货文章,建议收藏起来,方便时常学习与回顾,温故而知新。
  3. 创作不易,免费的点赞、关注,请走上一走,算是对博主一些鼓励,让我更有动力输出更多的干货内容。

什么是解释器模式

        解释器模式(Interpreter Pattern)是一种行为型设计模式,给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例,其核心原理是将一个语言表达式表示为一个抽象语法树,然后定义解释器来遍历这棵语法树并执行相应的操作。

        是的,没有看错,这就是解释器模式的标准定义,是不是和我一样,看完后完全处于懵逼状态,这里语言是什么?文法又是什么?语法树是什么?解释器是怎么解释句子的?...讲真的,解释器模式确实不太好理解。

        复杂的事情,慢慢来理解,先来弄懂标题解释器是什么意思?

        解释器(Interpreter)是一种电脑程序,能够把高级编程语言一行一行直接转译运行。实际它是一种翻译程序,执行方式是一边翻译一边执行。什么意思呢?再具体来说,比如JavaScript、Python就属于解释型语言,如下面的python代码,打印输出“hello world”,在计算机里是怎么运行呢?首先,计算机能够理解并执行的指令是二进制的,这些高级字符肯定是不直接执行的,因此在执行前,这句代码会通过python环境中的解释器解释成计算机可以理解的二进制指令,如果语法有问题,解释的过程就会出现异常,程序执行会中断。而Java是属于编译型的,什么意思呢?就是先把.java文件编译成.class文件然后再加载到虚拟机执行。

print("Hello, world!")

找到标题的出处后,后面的定义内容就好理解多了。

  • 语言:python就是一门编程语言,汉语就是一门中国人之间直接交流的语言;
  • 文法:就是语法,python有自己语法,遵循语法规范,程序才能正确解释并执行。汉语也有,一般就是主谓宾那一套;
  • 句子:对于python来说,print("Hello, world!"),就一句话。对于汉语来说,“我爱你,中国!”也是一个句子;
  • 语法树:语法树是句子结构的一种树型表示,比如:我爱你,中国!这句话照汉语的语法表示为一个树型结果就是:

解释器模式有哪些核心角色

解释器模式的核心角色主要包括以下四个:

  • 抽象表达式(IExpression):这是解释器模式中的核心接口,负责定义解释方法interpret,交由具体子类进行具体解释。
/**
 * 抽象表达式
 */
public abstract class AbstractExpression {

    public abstract boolean interpret(String info);
}
  • 终结符表达式(TerminalExpression):这是抽象表达式的子类,实现了与文法中终结符相关的解释操作。通常一个解释器中只有一个终结符表达式,但有多个实例,对应不同的终结符。
/**
 * 终结符表达式
 */
public class TerminalExpression extends AbstractExpression{
    private String data;

    public TerminalExpression(String data) {
        this.data = data;
    }

    @Override
    public boolean interpret(String info) {
        return info.contains(data);
    }
}
  • 非终结符表达式(NonterminalExpression):这也是抽象表达式的子类,实现了文法中与非终结符相关的解释操作。
/**
 * 非终结符表达式
 */
public class NonterminalExpression extends AbstractExpression {
    private AbstractExpression expr1;
    private AbstractExpression expr2;

    public NonterminalExpression(AbstractExpression expr1, AbstractExpression expr2) {
        this.expr1 = expr1;
        this.expr2 = expr2;
    }

    @Override
    public boolean interpret(String info) {
        return expr1.interpret(info) || expr2.interpret(info);
    }
}
  • 上下文环境类(Context):这个类主要用来存放解释器之外的全局信息,一般用来存放文法中各个终结符所对应的具体值。
/**
 * 上下文环境类
 */
public class Context {
    private AbstractExpression expression;

    public Context(AbstractExpression expression) {
        this.expression = expression;
    }
    public void  interpret(String info){
        System.out.println(info);
        boolean interpret = this.expression.interpret(info);
        if (interpret) {
            System.out.println("结束");
        }
    }
}
/**
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        AbstractExpression expre1=new TerminalExpression("\n");
        AbstractExpression expre2=new TerminalExpression("\r");
        AbstractExpression expre=new NonterminalExpression(expre1,expre2);
        Context context=new Context(expre);
        context.interpret("hello ,world!\n");

    }
}

解释器模式如何实现

需求描述

        这篇文章是设计模式系列的最后一篇,之所以放在最后一篇是因为我觉得理解起来比较困难和抽象。当然,如果你能够跟上我的思路,到这里也许已经明白其中的一二,再举一个实际的例子,来模拟解释器模式的实现,看完之后,也许就全明白了。

        现在直播带货是真的火,有的主播,随随便便一场直播,就够普通打工人干一辈子的了,也正因此,无数人开始蜂拥而入,更有很多人为了圈钱,更是没有底线的蹭热点、制造热点。人多了,那么问题就出现了:在直播间里,有的人比较喜欢这个主播,,则是“我爱你xxx”、“xxx,我支持你”之类的各种弹幕满屏飞,有的人不喜欢这个主播,则是各种诋毁和谩骂,那么如果需要对所有要输出打印到公屏上的弹幕做一个敏感词的过滤,应该怎么做呢?如果使用解释器模式,则可以这样实现:

实现方法

1、AbstractExpression.java:声明一个抽象表达式(IExpression):在抽象类里,定义解释方法interpret,由具体敏感词表达式、非敏感词表达工子类进行具体实现。

/**
 * 抽象表达式
 */
public abstract class AbstractExpression {

    public abstract boolean interpret(String info);
}

2、SensitiveExpression.java、NonsensitiveExpresson.java:声明具体的敏感词表达式类、非敏感词表达式类,实现具体的解释逻辑;

/**
 * 敏感词表达式
 */
public class SensitiveExpression extends AbstractExpression {
    private String data;

    public SensitiveExpression(String data) {
        this.data = data;
    }

    @Override
    public boolean interpret(String info) {
        return info.contains(data);
    }
}
/**
 * 非敏感词表达式
 */
public class NonsensitiveExpression extends AbstractExpression {
    private AbstractExpression expr1;
    private AbstractExpression expr2;

    public NonsensitiveExpression(AbstractExpression expr1, AbstractExpression expr2) {
        this.expr1 = expr1;
        this.expr2 = expr2;
    }

    @Override
    public boolean interpret(String info) {
        return expr1.interpret(info) || expr2.interpret(info);
    }
}

3、Context.java:上下文环境类(Context),持有具体的表达式实现。

/**
 * 上下文环境类
 */
public class Context {
    private AbstractExpression expression;

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

    public void print(String info) {

        boolean interpret = this.expression.interpret(info);
        if (interpret) {
            System.out.println("忽略敏感词:*****");
        } else {
            System.out.println("XXXX说:"+info);
        }
    }
}

4、编写业务客户端进行测试验证

/**
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        AbstractExpression expre1=new SensitiveExpression("滚");
        AbstractExpression expre2=new SensitiveExpression("垃圾");
        AbstractExpression expre=new NonsensitiveExpression(expre1,expre2);
        Context context=new Context(expre);
        context.print("支持主播");
        context.print("主播垃圾");
        context.print("主播真漂亮");
        context.print("主播滚蛋");

    }
}

解释器模式适用哪些场景

解释器模式的应用场景包括但不限于以下几种情况:

  1. 表达式求值:例如计算器应用、公式计算、数据分析等,解释器可以将表达式解析成计算机可以执行的指令。
  2. 自然语言处理:例如语言翻译、语音识别、虚拟助手等,解释器可以将人类语言翻译成计算机可以识别的语言。
  3. 日志处理:使用脚本语言或编程语言处理日志时,有很多服务会产生大量的日志,需要对日志进行解析,生成报表。各个服务的日志格式不同,数据中的要素相同,这种情况下,通过程序解决上述问题,主要的解决方案就是使用解释器模式。

解释器模式的优点和缺点

优点

  1. 易于改变和扩展文法:由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
  2. 每一条文法规则都可以表示为一个类:因此可以方便地实现一个简单的语言。
  3. 增加新的解释表达式较为方便:如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合“开闭原则”。

缺点

  1. 对于复杂文法难以维护:在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护。
  2. 执行效率较低:由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。

总结

总之,在表达式求值、自然语言处理和日志处理等业务场景中,解释器模式可以将人类语言或非编程语言转换为计算机可执行的语言,从而实现对数据的解析、处理和计算等功能,是一种行之有效的设计模式,但是其本身复杂性和核心原理需要特别注意。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GEE20:获取地面站点的遥感图层的采样值

获取采样点值 1. 介绍2. GEE实现2.1 数据介绍2.2 GEE code 3.参考 1. 介绍 去年由于实验需要,想通过GEE获取遥感图层的采样值,但是多次尝试后任无法实现。最近通过查询,终于找到的获取采样点的方法,现在将其记录在此,并…

《视觉SLAM十四讲》-- 视觉里程计2

文章目录 07 视觉里程计 27.1 直接法的引出7.2 2D 光流7.2.1 Lucas-Kanade 光流7.2.1 实践:LK 光流 7.3 直接法7.3.1 推导过程7.3.2 直接法的优缺点 07 视觉里程计 2 7.1 直接法的引出 特征点的缺点: 关键点的提取与描述子的计算非常耗时,实…

辐射骚扰整改思路及方法:对差模电流的影响?|深圳比创达电子EMC

同样的分析可以应用在差模电流的情况,唯一不同的是,两根导线的磁场分布是相反的,磁场互相抵消,由于导线中的电流大小相等,产生的磁场H大小相同,在互相靠近的情况下,在磁环内的两个感应磁场大小相…

C盘可以格式化吗?正确答案在这里!

“我一直有个比较好奇的问题,我的电脑中病毒了,我想将c盘进行格式化操作,但是c盘中是有比较多重要文件的,在这种情况下,c盘可以格式化吗?怎么格式化c盘呢?” 面对各种电脑情况,可能不…

Python实现cpu密集型多线程

问题: 想通过以下代码实现多线程 class ThreadTest(threading.Thread):def __init__(self, num, *args, **kwargs):super(ThreadTest, self).__init__(*args, **kwargs)self.num numprint(num)def run(self):print(self.num)print(time.time())if __name__ &quo…

postman连接数据库

参考:https://blog.csdn.net/qq_45572452/article/details/126620210 1、安装node.js 2、配置环境变量 3、安装xmysql连接数据库cmd窗口输入"npm install -g xmysql"后回车cmd窗口输入"xmysql"后回车,验证xmysql是否安装成功(下图代表安装成功)…

编程最佳外挂:批量数据分析与可视化,CodeGeeX工具箱一键完成

ChatGLM3代模型的Code Interpreter能力,本周已经在VSCode里的CodeGeeX插件产品中,以开发者工具箱的产品形态上线。 下图以VSCode插件为例:在CodeGeeX的侧边栏,和智能问答AskCodeGeeX并列出现的工具箱标签,用户登录后就…

区块链拆分

随着区块链技术的发展和普及,去中心化钱包逐渐成为数字货币领域的重要工具。去中心化钱包不仅具有高度安全性和隐私保护能力,还可以通过智能合约和开源技术实现定制化功能。本文将探讨去中心化钱包定制开发的基本概念、优势、流程和前景。 一、去中心化钱…

YOLOv8 营业执照提取 统一社会信用代码、企业名称

目录 背景 尝试一:整图OCR识别,然后正则匹配 尝试二:利用显著特征,直接传统方法定位,切出来识别 尝试三:yolov8训练一个统一社会信用代码、企业名称位置检测 ​编辑 效果 模型信息 项目 ​编辑 代…

(六)Spring源码解析:Spring AOP源码解析

一、AOP概念 Aspect:切面 给业务方法增加到功能,切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面是通知(Advice)。实际就是对主业务逻辑的一种增强。 Pointcut:切入点 切入点指声明的…

springcloudalibaba入门详细使用教程

目录标题 一、简介二、SpringCloud Alibaba核心组件2-1、Nacos (配置中心与服务注册与发现)2-2、Sentinel (分布式流控)2-3、RocketMQ (消息队列)/RabbitMq/kafka2-4、Seata (分布式事务)2-5、Dubbo (RPC) 三、为什么大家看好 Spring Cloud Alibaba3-1、阿里巴巴强大的技术输出…

【每日一题】307. 区域和检索 - 数组可修改-2023.11.13

题目: 307. 区域和检索 - 数组可修改 给你一个数组 nums ,请你完成两类查询。 其中一类查询要求 更新 数组 nums 下标对应的值另一类查询要求返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 和 &#xff0c…

智能井盖传感器具有什么效果?

智能井盖传感器与智慧城市之间有着密切的关联,两者之间属于相辅相成的状态,对于城市的现代化和城市生命线建设有助力作用。智能井盖传感器是其中一个重要的组成环节,它们帮助城市改变原有的生活和生态环境,为政府部门完善城市基础…

【机器学习】 朴素贝叶斯算法:原理、实例应用(文档分类预测)

1. 算法原理 1.1 朴素贝叶斯方法 朴素贝叶斯方法涉及一些概率论知识,我们先来复习一下。 联合概率:包含多个条件,并且所有的条件同时成立的概率,公式为: 条件概率:事件A在另一个事件B已经发生的前提下发…

工作十年+的测试应该具备什么能力?

大概是2014年的时候,我开始接触面试工作,就是从应聘者转为面试官,记得印象深刻的是面试了一位做了8年的测试。对方气场很足,嗯,毕竟那时的我还只是一个3、4年经验的小测试,相反,印象深刻的并不是…

Mysql基本知识

1.SQL分类 DDL【data definition language】 数据定义语言,用来维护存储数据的结构 代表指令: create, drop, alter DML【data manipulation language】 数据操纵语言,用来对数据进行操作 代表指令: insert,delete,up…

十四、W5100S/W5500+RP2040树莓派Pico<NetBIOS>

文章目录 1 前言2 简介2 .1 什么是NetBIOS?2.2 NetBIOS的优点2.3 NetBIOS工作原理2.4 NetBIOS应用场景 3 WIZnet以太网芯片4 NetBIOS网络设置示例概述以及使用4.1 流程图4.2 准备工作核心4.3 连接方式4.4 主要代码概述4.5 结果演示 5 注意事项6 相关链接 1 前言 随着…

单片机定时器讲解和实现

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、计数器是什么?二、单片机定时器结构2.1***两个8位如何合成16位,16位如何分成两个8位***2.2 计数器的位数组合?2.3 定时功…

C# OpenCvSharp 基于直线检测的文本图像倾斜校正

效果 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using OpenCvSharp;namespace OpenCvSharp_基于直线检测的文本图像…

基于若依的ruoyi-nbcio流程管理系统增加读取节点扩展属性的方法

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码: https://gitee.com/nbacheng/ruoyi-nbcio 演示地址:RuoYi-Nbcio后台管理系统 我们的在流程设计器里会根据需要再不同的节点增加扩展属性,如何动态读取这些扩展属性&#xff…