行为型模式-解释器模式

news2025/1/16 13:57:19

1.概述

在这里插入图片描述
如上图,设计一个软件用来进行加减计算。我们第一想法就是使用工具类,提供对应的加法和减法的工具方法。

//用于两个整数相加
public static int add(int a,int b){
	return a + b;
}
//用于两个整数相加
public static int add(int a,int b,int c){
	return a + b + c;
}
//用于n个整数相加
public static int add(Integer ... arr) {
	int sum = 0;
	for (Integer i : arr) {
		sum += i;
	}
	return sum;
}

上面的形式比较单一、有限,如果形式变化非常多,这就不符合要求,因为加法和减法运算,两个运算
符与数值可以有无限种组合方式。比如 1+2+3+4+5、1+2+3-4等等。

显然,现在需要一种翻译识别机器,能够解析由数字以及 + - 符号构成的合法的运算序列。如果把运算符和数字都看作节点的话,能够逐个节点的进行读取解析运算,这就是解释器模式的思维。

定义

给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。

在解释器模式中,我们需要将待解决的问题,提取出规则,抽象为一种“语言”。比如加减法运算,规则为:由数值和±符号组成的合法序列,“1+3-2” 就是这种语言的句子。

解释器就是要解析出来语句的含义。但是如何描述规则呢?

文法(语法)规则:
文法是用于描述语言的语法结构的形式规则。

expression ::= value | plus | minus
plus ::= expression ‘+’ expression
minus ::= expression ‘-’ expression
value ::= integer
注意: 这里的符号“::=”表示“定义为”的意思,竖线 | 表示或,左右的其中一个,引号内为字符本身,引号外为语法。

上面规则描述为 :

表达式可以是一个值,也可以是plus或者minus运算,而plus和minus又是由表达式结合运算符构
成,值的类型为整型数。

抽象语法树
在计算机科学中,抽象语法树(AbstractSyntaxTree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。

用树形来表示符合文法规则的句子
在这里插入图片描述

2.结构

解释器模式包含以下主要角色。

  • 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
  • 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
  • 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
  • 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
  • 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。

3.案例实现

【例】设计实现加减法的软件
在这里插入图片描述
代码如下

package com.itheima.pattern.interpreter;

/**
 * @program: design-patterns
 * @ClassName AbstractExpression
 * @description: 抽象表达式类
 * @author: 
 * @create: 2023-02-04 08:29
 * @Version 1.0
 **/
public abstract class AbstractExpression {
    public abstract int interpret(Context context);
}
package com.itheima.pattern.interpreter;

/**
 * @program: design-patterns
 * @ClassName Variable
 * @description: 封装变量的类
 * @author: 
 * @create: 2023-02-04 08:31
 * @Version 1.0
 **/
public class Variable extends AbstractExpression {
    // 声明存储变量名的成员变量
    private String name;

    public Variable(String name) {
        this.name = name;
    }

    @Override
    public int interpret(Context context) {
        // 返回变量的值
        return context.getValue(this);
    }

    @Override
    public String toString() {
        return name;
    }
}
package com.itheima.pattern.interpreter;

/**
 * @program: design-patterns
 * @ClassName Plus
 * @description: 加法表达式类(非终结符表达式)
 * @author: 
 * @create: 2023-02-04 08:35
 * @Version 1.0
 **/
public class Minus extends AbstractExpression {
    // -左边的表达式
    private AbstractExpression left;
    // -右边的表达式
    private AbstractExpression right;

    public Minus(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret(Context context) {
        // 将左边表达式的结果与右边表达式的结果相加
        return left.interpret(context) - right.interpret(context);
    }

    @Override
    public String toString() {
        return "(" + left.toString() + " - " + right.toString() + ")";
    }
}
package com.itheima.pattern.interpreter;

/**
 * @program: design-patterns
 * @ClassName Plus
 * @description: 加法表达式类(非终结符表达式)
 * @author: 
 * @create: 2023-02-04 08:35
 * @Version 1.0
 **/
public class Plus extends AbstractExpression {
    // +左边的表达式
    private AbstractExpression left;
    // +右边的表达式
    private AbstractExpression right;

    public Plus(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret(Context context) {
        // 将左边表达式的结果与右边表达式的结果相加
        return left.interpret(context) + right.interpret(context);
    }

    @Override
    public String toString() {
        return "(" + left.toString() + " + " + right.toString() + ")";
    }
}
package com.itheima.pattern.interpreter;

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

/**
 * @program: design-patterns
 * @ClassName Context
 * @description: 环境角色类
 * @author: 
 * @create: 2023-02-04 08:30
 * @Version 1.0
 **/
public class Context {
    // 定义一个Map集合用来存储变量及对应的值
    private Map<Variable, Integer> map = new HashMap<>();

    // 添加变量的功能
    public void assign(Variable var, Integer value) {
        map.put(var, value);
    }

    // 根据变量获取对应的值
    public int getValue(Variable variable) {
        return map.get(variable);
    }
}
package com.itheima.pattern.interpreter;

/**
 * @program: design-patterns
 * @ClassName Client
 * @description: 测试类
 * @author: 
 * @create: 2023-02-04 08:46
 * @Version 1.0
 **/
public class Client {
    public static void main(String[] args) {
        // 创建环境对象
        Context context = new Context();
        // 创建多个变量对象
        Variable a = new Variable("a");
        Variable b = new Variable("b");
        Variable c = new Variable("c");
        Variable d = new Variable("d");

        // 将变量存储到环境对象中
        context.assign(a, 1);
        context.assign(b, 2);
        context.assign(c, 3);
        context.assign(d, 4);

        // 获取抽象语法树
        AbstractExpression expression = new Minus(a, new Plus(new Minus(b, c), d));
        // 解释计算
        int result = expression.interpret(context);

        System.out.println(expression + " = " + result);
    }
}

4.优缺点

  • 优点
    • 易于改变和扩展文法。
      由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
      每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
    • 实现文法较为容易。
      在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂。
    • 增加新的解释表达式较为方便。
      如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原
      有表达式类代码无须修改,符合 “开闭原则”。
  • 缺点
    • 对于复杂文法难以维护。
      在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个
      数将会急剧增加,导致系统难以管理和维护。
    • 执行效率较低。
      由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而
      且代码的调试过程也比较麻烦。

5.使用场景

  • 当语言的文法较为简单,且执行效率不是关键问题时。
  • 当问题重复出现,且可以用一种简单的语言来进行表达时。
  • 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候。

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

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

相关文章

OAuth2(三)

首先把项目在本地运行起来 注意redis的配置 在地址栏输入 自动跳断点 界面截图

.NET MAUI 开发电子木鱼(上)

本文介绍如何使用 .NET MAUI 开发一个电子木鱼应用。以实际的小应用开发为例&#xff0c;通过这个开发过程&#xff0c;介绍了其涉及的 .NET MAUI、Blazor、前端等相关知识点。文章涉及的应用已开源在 Github&#xff0c;大家可前往下载体验&#xff1a; https://github.com/sa…

[架构之路-99]:《软件架构设计:程序员向架构师转型必备》-9-确定关键性需求与决定系统架构的因素

第9章 确定关键性需求与决定系统架构的因素9.1 概念架构是什么9.1.1 概念架构是直指目标的设计思想、重大选择9.1.2 案例1&#xff1a;汽车电子AUTOSAR——跨平台复用NA9.1.3 案例2&#xff1a;腾讯QQvideo架构——高性能NA9.1.4 案例3&#xff1a;微软MFC架构——简化开发NA9.…

断网后,是否能够ping通127.0.0.1?

引言 说起这个问题很搞笑&#xff0c;其实也是挺有意思的。是这么回事&#xff0c;公司突然断网了&#xff0c;有人突然来了一句&#xff0c;断网了&#xff0c;能不能ping通127.0.0.1呢&#xff1f;大家就实验起来了&#xff01; 结果显而易见&#xff0c;如上图&#xff0c;…

什么时候可以不进行测试?

如果存在任何原因导致不需要使用测试结果提供的信息&#xff0c;就没有必要进行测试。测试得到的信息不可靠&#xff0c;也没有必要测试。 1、测试后风险增加 软件行业的经理经常需要做出带风险的决定&#xff0c;通常在获得部分信息的情况下做出决定是比较保险的。但有些时候…

沿着数字中国的大江大河,领略云上三峡

长年以来&#xff0c;提到沿江旅行&#xff0c;国人脑海中浮现的画面一定是三峡。而在今天&#xff0c;沿着数字中国的大江大河溯源而上&#xff0c;也会看到一座云上三峡。郦道元在《水经注》里是这样描写三峡的“至于夏水襄陵&#xff0c;沿溯阻绝。有时朝发白帝&#xff0c;…

Docker - 11. 本地镜像发布到私有库

1. 为什么要有私有库&#xff1f; 如果涉及到公司机密文件&#xff0c;使用DockerHub、阿里云这样的公共镜像仓库就不合适&#xff0c;所以需要创建一个本地私人仓库提供给团队使用&#xff0c;基于公司内部项目构建镜像。而 Docker Registry是官方提供的工具&#xff0c;可以…

【技术应用】java实现排行榜方案

【技术应用】java实现排行榜方案一、前言二、实现方案方案一、通过数据库实现方案二、通过集合List实现数据排序功能方案三、通过redis的zset实现方案四、通过java中的sortedSet集合实现方案五、通过java的priorityQueue队列实现一、前言 最近在做一个项目的性能优化&#xff…

12、获取字符串信息

目录 一、获取字符串长度 二、字符串查找 &#xff08;1&#xff09;indexOf(String s) &#xff08;2&#xff09;lastIndexOf(String str) 三、获取指定索引位置的字符 一、获取字符串长度 使用String类的length()方法可获取声明的字符串对象的长度。 语法如下&#x…

Linux Centos9 Stream 安装mysql8

安装mysql8教程前言安装Mysql8.0使用Mysql yum 存储库进行安装。安装mysql8.0启动mysql 服务创建用户完成安装使用Navicat 连接刚装好的mysql如果博主的文章对您有所帮助&#xff0c;可以评论、点赞、收藏&#xff0c;支持一下博主!!!前言 操作系统&#xff1a;Linux Centos9 …

JAVA-Spring Bean作用域

目录 基本概念 Bean 作用域 spring支持的bean作用域有哪些&#xff1f; 近日研究Spring和SpringBoot的一些内容&#xff0c;给大家做一些分享&#xff0c;请大家多多提出您的宝贵意见。 学习知识要了解其涉及到的基本概念&#xff0c;才能理解这个知识&#xff0c;并且做到…

八种排序算法

文章目录1、冒泡排序1.基本思路2.代码实现3.时间复杂度和空间复杂度2、快速排序1.基本思路2.代码实现3.时间复杂度和空间复杂度3、直接插入1.基本思路2.代码实现3.时间复杂度和空间复杂度4、希尔排序1.基本思路2.代码实现3.时间复杂度和空间复杂度5、简单选择1.基本思路2.代码实…

数据库管理系统有哪些

文章目录RDBMS非RDBMSDocumentKey-valueGraphhttps://db-engines.com/en/ranking该网站根据各 DBMS的流行度&#xff0c;列出了它们的排名&#xff0c;每月更新一次。当前是2023年2月份的排名。DataBase Model这一列中显示了各 DBMS所使用的 数据模型&#xff0c;有的使用了单个…

SpringAMQP从0到1

初识MQ 同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得到响应&#xff0c;但是你却…

Redis最佳实践 | 黑马Redis高级篇

目录 一、Redis键值设计 1、优雅的key结构 2、BigKey问题 什么是BigKey BigKey的危害 如何发现BigKey 如何删除BigKey 3、恰当的数据类型 二、批处理优化 1、Pipeline 大量数据导入的方式 MSET Pipeline 2、集群下的批处理 三、服务端优化 1、持久化配置 2、慢…

MyBatis案例 | 使用映射配置文件实现CRUD操作——多条件查询

本专栏主要是记录学习完JavaSE后学习JavaWeb部分的一些知识点总结以及遇到的一些问题等&#xff0c;如果刚开始学习Java的小伙伴可以点击下方连接查看专栏 本专栏地址&#xff1a;&#x1f525;JavaWeb Java入门篇&#xff1a; &#x1f525;Java基础学习篇 Java进阶学习篇&…

【论文阅读】Exathlon: A Benchmark for Explainable Anomaly Detection over Time Series

论文来源 标题: Exathlon: A Benchmark for Explainable Anomaly Detection over Time Series (Vincent Jacob,2021) 作者: Vincent Jacob, Fei Song, Arnaud Stiegler, Bijan Rad, Yanlei Diao, Nesime Tatbul 期刊: Proceedings of the VLDB Endowment 研究问题 Exathlon是…

尚医通(三)医院设置模块后端 | swagger | 统一日志 | 统一返回结果

目录一、医院设置模块需求二、医院设置表结构三、医院模块配置四、医院查询功能1、创建包结构&#xff0c;创建SpringBoot启动类2、编写controller代码3、创建SpringBoot配置类5、运行启动类6、统一返回的json时间格式五、医院设置逻辑删除功能1、HospitalSetController添加删除…

CDA Level Ⅱ 模拟题(二)

练习题 【单选题】1/20 一项针对全国25-35岁用户群的手机喜好调查&#xff0c;但调研项目经费大概是10万元&#xff0c;并且用户群相对集中在中国中部城市。前期预调研显示&#xff0c;用户群的数值方差和调研费用不等。以下哪种情况是比较适宜的调查方式&#xff1f; A.简单随…

【C++入门】

目录1、命名空间1.1、命名空间定义1.2、命名空间的使用2、C输入和输出3、缺省参数3.1 缺省参数概念3.2缺省参数分类4、函数重载4.1、函数重载概念4.2 C支持函数重载的原理--名字修饰5、引用5.1、引用概念5.2、引用特性5.3、常引用5.4、使用场景5.5、传值、传引用效率比较5.6、引…