【Java开发】设计模式 12:解释器模式

news2025/1/12 7:59:46

1 解释器模式介绍 

解释器模式是一种行为型设计模式,它提供了一种方法来解释语言、表达式或符号。

在该模式中,定义了一个表达式接口,并实现了对应的表达式类,这些类可以解释不同的符号组成的表达式,从而实现对语言的解释。

📌 场景

这种模式常常被用在编译器、解释器和正则表达式库中。该模式的核心思想是将一个复杂的问题分解成许多简单的问题,并将这些问题之间的关系表示为一种语法规则。

📌 优点

  1. 灵活性好:解释器模式能够灵活地处理复杂的或变化的语法规则,因为语法规则都是由解释器来进行解释和处理的。
  2. 易于修改和扩展:由于解释器模式中的文法以类的形式存在,所以增加新的文法规则和解释器相对容易。

📌 缺点

  1. 执行效率较低:因为使用解释器模式需要对文法规则进行解析和执行,而这个过程是比较耗时的,所以解释器模式的执行效率较低。
  2. 增加系统复杂性:因为解释器模式中的文法规则以类的形式存在,所以随着文法规则的增加,类的数量也会随之增加,这会增加系统的复杂性。
  3. 可维护性较差:由于解释器模式中的文法规则以类的形式存在,如果系统中存在大量的文法规则,那么系统的维护难度也会相应增加。

📌 注意

解释器模式在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题。如果碰到对表达式的解释,在 Java 中可以用 Expression4J 或 Jep 等来设计。

📌 四大角色

  • 抽象表达式(Expression) :负责定义解释方法 interpret, 交由子类进行具体解释。
  • 终结符表达式(Terminal Expression) :实现文法中与终结符有关的解释操作。文法中的每一个终结符都有一个具体终结表达式与之相对应,比如公式 R = R1 + R2,R1 和 R2 就是终结符,对应的解析 R1 和 R2 的解释器就是终结符表达式。通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。
  • 非终结符表达式(Nonterminal Expression):实现文法中与非终结符有关的解释操作。文法中的每条规则都对应于一个非终结符表达式。非终结符表达式一般是文法中的运算符或者其他关键字,比如公式 R = R1 + R2 中, + 就是非终结符,解析它的解释器就是一个非终结符表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
  • 上下文环境(Context) :包含解释器之外的全局信息。它的任务一般是用来存放文法中各个终结符所对应的具体值,比如 R = R1 + R2,给 R1 赋值 100,给 R2 赋值 200,这些信息需要存放到环境中。

2 解释器模式实现

以根据乘客年龄和身高来判断乘坐公交车是否免费为例:

📌 1.定义乘客

/**
 * 乘客
 */
@Data
@AllArgsConstructor
public class Passenger {
    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private Integer age;

    /**
     * 身高
     */
    private Double height;
}

📌 2.定义表达式

/**
 * 表达式
 */
public interface Expression {
    /**
     * 解释年龄
     * @param age 年龄
     * @return 解释结果
     */
    boolean interpret(int age);

    /**
     * 解释身高
     * @param height 身高
     * @return 解释结果
     */
    boolean interpret(double height);
}

📌 3.定义比较器(枚举)

/**
 * 比较器
 */
public enum Compare {
    /**
     * 较大
     */
    GT,
    /**
     * 相等
     */
    EQ,
    /**
     * 较小
     */
    LT
}

📌 4.定义终结符表达式

/**
 * 终结符表达式
 */
public class TerminalExpression implements Expression {
    /**
     * 年龄
     */
    private Integer age;

    /**
     * 身高
     */
    private Double height;

    /**
     * 比较器
     */
    private final Compare compare;

    /**
     * 构造年龄比较
     * @param age 年龄
     * @param compare 比较器
     */
    public TerminalExpression(int age, Compare compare) {
        this.age = age;
        this.compare = compare;
    }

    /**
     * 构造身高比较
     * @param height 身高
     * @param compare 比较器
     */
    public TerminalExpression(double height, Compare compare) {
        this.height = height;
        this.compare = compare;
    }

    @Override
    public boolean interpret(int age) {
        // 比较年龄大小
        switch (compare) {
            // 较大
            case GT:
                return age > this.age;
            // 相等
            case EQ:
                return age == this.age;
            // 较小
            case LT:
                return age < this.age;
            default:
                return false;
        }
    }

    @Override
    public boolean interpret(double height) {
        // 比较身高大小
        switch (compare) {
            // 较大
            case GT:
                return height > this.height;
            // 相等
            case EQ:
                return height == this.height;
            // 较小
            case LT:
                return height < this.height;
            default:
                return false;
        }
    }
}

📌 5.定义非终结符表达式

①与表达式:

/**
 * 与表达式
 */
public class AndExpression implements Expression {
    /**
     * 表达式1
     */
    private Expression expression1;
    /**
     * 表达式2
     */
    private Expression expression2;
    /**
     * 构造表达式
     * @param expression1 表达式1
     * @param expression2 表达式2
     */
    public AndExpression(Expression expression1, Expression expression2) {
        this.expression1 = expression1;
        this.expression2 = expression2;
    }
    @Override
    public boolean interpret(int age) {
        return this.expression1.interpret(age) && this.expression2.interpret(age);
    }
    @Override
    public boolean interpret(double height) {
        return this.expression1.interpret(height) && this.expression2.interpret(height);
    }
}

②或表达式:

/**
 * 或表达式
 */
public class OrExpression implements Expression {
    /**
     * 表达式1
     */
    private Expression expression1;

    /**
     * 表达式2
     */
    private Expression expression2;

    /**
     * 构造表达式
     * @param expression1 表达式1
     * @param expression2 表达式2
     */
    public OrExpression(Expression expression1, Expression expression2) {
        this.expression1 = expression1;
        this.expression2 = expression2;
    }
    @Override
    public boolean interpret(int age) {
        return this.expression1.interpret(age) || this.expression2.interpret(age);
    }
    @Override
    public boolean interpret(double height) {
        return this.expression1.interpret(height) || this.expression2.interpret(height);
    }
}

📌 6.定义免费标准

/**
 * 免费标准
 */
public class Free {
    /**
     * 年龄表达式
     */
    private Expression ageExpression;

    /**
     * 身高表达式
     */
    private Expression heightExpression;

    /**
     * 构造免费情况
     * @param age 年龄
     * @param height 身高
     */
    public Free(int age, double height) {
        // 大于等于设定年龄
        Expression expression1 = new TerminalExpression(age, Compare.GT);
        Expression expression2 = new TerminalExpression(age, Compare.EQ);
        ageExpression = new OrExpression(expression1, expression2);
        // 小于等于设定身高
        expression1 = new TerminalExpression(height, Compare.LT);
        expression2 = new TerminalExpression(height, Compare.EQ);
        heightExpression = new OrExpression(expression1, expression2);
    }

    /**
     * 结果
     * @param age 年龄
     * @param height 身高
     * @return 判定结果
     */
    public boolean adjust(int age, double height) {
        return ageExpression.interpret(age) || heightExpression.interpret(height);
    }
}

📌 7.调用

// 定义乘客集合
List<Passenger> list = new ArrayList<>();
Passenger p1 = new Passenger("张三", 65, 170.0);
Passenger p2 = new Passenger("李四", 10, 130.0);
Passenger p3 = new Passenger("王五", 50, 170.0);
list.add(p1);
list.add(p2);
list.add(p3);

list.forEach(p->{
    // 定义免费标准
    Free free = new Free(65, 130);
    // 满足条件则免费
    if (free.adjust(p.getAge(), p.getHeight())) {
        System.out.println(p.getName() + ":免费");
    }// 不满足条件则正常收费
    else {
        System.out.println(p.getName() + ":刷卡或投币");
    }
});

控制台输出:

可以看到,这里按照预期输出了结果,实现了根据年龄和身高自动判断是否免费的功能。

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

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

相关文章

云原生之在kubernetes环境下部署wordpress

云原生之在kubernetes环境下部署wordpress 一、wordpress介绍1.wordpress简介2.wordpress特点 二、kubernetes集群介绍1.k8s简介2.k8s架构图 三、本次实践介绍1.本次实践简介2.本次环境规划 四、环境准备工作1.检查k8s环境2.检查系统pod状态 五、创建mysql的secret资源1.创建部…

OpenCV_contrib配置教程(详细版)

文章目录 一&#xff1a;前提准备1&#xff1a;OpenCV4.5.1、OpenCV_contrib4.5.1扩展库下载2&#xff1a;Cmake下载地址Download 二&#xff1a;cmake配置1&#xff1a;2&#xff1a; 三&#xff1a;vs2017编译OpenCV build文件四&#xff1a;环境配置 个人笔记&#xff1a; …

如何构建编译gopacket包的测试用例?(gopacket抓取数据包、类似于tcpdump、Wireshark)

本篇文章介绍一下&#xff0c;如何构建gopacket测试用例 1、构建过程中遇到的问题 问题1&#xff1a; -bash: ./gopacket-main: /lib/ld-musl-x86_64.so.1: bad ELF interpreter: No such file or directory问题2&#xff1a; #14 3.150 # github.com/google/gopacket/pcap …

Zimbra 远程代码执行漏洞(CVE-2019-9670)环境搭建

漏洞简介 Zimbra是著名的开源系统&#xff0c;提供了一套开源协同办公套件包括WebMail&#xff0c;日历&#xff0c;通信录&#xff0c;Web文档管理和创作。一体化地提供了邮件收发、文件共享、协同办公、即时聊天等一系列解决方案。此漏洞的主要利用手法是通过 XXE (XML 外部…

【附代码】SSVEP解码算法 - 多变量同步指数(MSI)

1 算法来源 该算法来自电子科技大学张杨松博士,针对该算法的计算在张博士的博士论文中有详细介绍,有兴趣的读者可以下载阅读,本文重点在对该方法的代码实现。Zhang, Yangsong, et al. “Multivariate synchronization index for frequency recognition of SSVEP-based brain…

JS和CSS实现的原生轮播图

文章目录 &#x1f357; 前言&#x1f356; JSCSS实现滑动轮播图&#x1f354; 纯CSS实现滑动轮播图&#x1f37f; JSCSS实现浅入浅出轮播图&#x1f96a; JSCSS实现滑动带遮罩轮播图&#x1f9c0; JSCSS实现卡片式轮播图 &#x1f357; 前言 图片来自百度图片&#xff0c;可以…

分析解决【No module named ‘triton‘】的问题

&#xff08;一&#xff09;现象 在Windows11下训练Stable-Diffusion的LoRA模型的时候&#xff0c;总是重复提示&#xff1a; A matching Triton is not available, some optimizations will not be enabled. Error caught was: No module named ‘triton’ 意思是&#xff1a…

RocketMQ之 Consumer,消费者消费原理解析

B站 https://www.bilibili.com/video/BV1rX4y1z72v在线学习文档 https://d9bp4nr5ye.feishu.cn/wiki/wikcnjjvso9uytlgVJBfKcJh1Kq 今天我们阅读源码的目的&#xff1a;在SpringBoot项目中&#xff0c;RocketMQ是如何通过 RocketMQMessageListener 来进行消费的。 在SpringBoot…

Python数据结构-----leetcode用队列实现栈

目录 前言&#xff1a; 方法步骤 示例 Python代码实现 225. 用队列实现栈 前言&#xff1a; 上一期学习了怎么去通过两个栈来实现队列&#xff0c;同样这一期我就来讲讲怎么去通过两个队列来实现栈的功能&#xff0c;一起来学习吧。&#xff08;上一期链接Python数据结构--…

【Git基础】常用git命令(一)

文章目录 1. 创建仓库1.1 创建仓库1.2 git add和git commit① git add② git commit③ 工作区、暂存区和仓库 2. 创建git服务器2.1 服务器&#xff1a;2.2 本地2.3 修改配置信息 3. git基础原理3.1 四个区域3.2 工作流程3.3 文件的四种状态① git rm② git checkout 4.优雅的提…

setup的两个注意点

setup的两个注意点 首先&#xff0c;我们原本在v2中&#xff0c;父组件给子组件传递参数时&#xff0c;使用props来接收&#xff0c;当然除了这个方法外&#xff0c;我们还可以通过$attr来接收&#xff0c;只不过使用$atter就不能对父组件传来的参数进行类型的限定&#xff0c…

你是一个资深API接口爬虫程序员,现在需要你介绍一下如何通过商品id来获取商品数据并读取出来

获取商品数据通常需要使用API接口&#xff0c;根据接口文档中的说明传递商品id参数&#xff0c;并使用相应的请求方式&#xff08;通常为GET请求&#xff09;向API服务器发送请求即可。 以下是一个获取商品数据的示例请求&#xff1a; 首先打开API接口文档&#xff0c;找到获…

小航助学答题系统编程等级考试scratch二级真题2023年3月(含题库答题软件账号)

青少年编程等级考试scratch真题答题考试系统请点击 电子学会-全国青少年编程等级考试真题Scratch一级&#xff08;2019年3月&#xff09;在线答题_程序猿下山的博客-CSDN博客_小航答题助手 1.小猫的程序如图所示&#xff0c;积木块的颜色与球的颜色一致。点击绿旗执行程序后&a…

USB TO SPI / USB TO I2C 软件概要 7 --- 专业版调试器

所需设备&#xff1a; 1、USB 转 SPI / I2C 适配器&#xff1b; 软件概述&#xff1a; SPI类: USB TO SPI 1.0-Slave SPI从机软件&#xff0c;适合单步调试&#xff0c;支持SPI工作模式0、1、2、3&#xff0c;自动跟随主机通讯速率&#xff0c;自动接收数据&#xff1b; …

【算法宇宙——在故事中学算法】背包dp之完全背包问题

学习者不灵丝相传&#xff0c;而自杖明月相反&#xff0c;子来此事却无得失。 文章目录 前言正文小明的探险之旅&#xff08;2&#xff09;最后的优化代码 前言 尽管计算机是门严谨的学科&#xff0c;但正因为严谨&#xff0c;所以要有趣味才能看得下去。在笔者的前几篇算法类…

C#基础学习--LINQ

什么是LINQ 从对象获取数据的方法一直都是作为程序的一部分而设计的&#xff0c;然而使用LINQ可以很轻松的查询对象集合 LINQ提供程序 匿名类型 匿名类型经常用于LINQ查询的结果之中 匿名类型的对象创建表达式&#xff1a; using System; using System.Collections; using …

BiFormer:基于双层路由注意力的视觉Transformer

文章目录 摘要1、简介2、相关工作3、我们的方法:BiFormer3.1、预备知识&#xff1a;注意力3.2、双层路由注意(BRA)3.3、BRA的复杂性分析 4、实验4.1、ImageNet-1K图像分类4.2. 目标检测与实例分割4.3. 基于ADE20K的语义分割4.4、消融研究4.5、注意图可视化 5、局限性和未来工作…

C++ -3- 类和对象(中) | (三)END

文章目录 6.日期类的实现构造函数赋值运算符 “”前置、后置日期 - 日期日期类实现—代码汇总流插入流提取 7.const成员const 与 权限放大 8.取地址及const取地址操作符重载 6.日期类的实现 #pragma once #include <stdbool.h> #include <iostream> using namespa…

vue vue-json-viewer 展示 JSON 格式数据

1、下载 vue-json-viewer npm 下载 vue-json-viewer &#xff1a; // Vue2 npm install vue-json-viewer2 --save // Vue3 npm install vue-json-viewer3 --saveyarn 下载 vue-json-viewer &#xff1a; // Vue2 yarn add vue-json-viewer2 // Vue3 yarn add vue-json-view…

基于LS1028 TSN 交换机软件系统设计与实现(三)

NXP 推出 OpenIL 作为用于工业领域的 Linux 发行版&#xff0c; OpenIL 新增的部分中 含有&#xff1a;支持实时的操作系统的扩展和支持工业厂房中自动化 OEM 的 Time-Sensitive 网络。 OpenIL 作为开放型的工业 Linux 系统最大的优势便是将实时计算在网络中 的…