Java设计模式之行为型-解释器模式(UML类图+案例分析)

news2024/11/28 16:30:47

目录

一、基础概念

二、UML类图

三、角色设计

四、案例分析

五、总结


 

一、基础概念

解释器模式是指给定一个语言(表达式),来表示它的文法,并定义一个解释器,使用该解释器来解释语言中的句子(表达式),并得到结果。简单来说,解释器模式把要处理的问题条条框框地定义出来,然后挨个细致解释,适合一些重复性的问题求解。

所谓终结符,是指表达式中的最小单位,它不再包含其他更小的表达式,可以理解为表达式的“叶子”节点,例如在一个简单的表达式计算器中:

1、数字值(如123)就是一个终结符,数字解释器实现了对数字的解释操作。

2、变量名(如a)也可以看作是一个终结符,变量解释器实现获取变量的值。

非终结符需要通过组合包含的子表达式的结果进行解释,例如:

1、加法表达式包含左边和右边两个子表达式,通过组合两个子表达式的计算结果实现加法。

2、函数调用表达式包含函数名和参数表达式,需要调用函数并传入参数计算结果。

举个例子:"5 + 6",然后解析这个表达式,计算出结果11。

在这个例子中:

1、数字5和6是终结符,它们是表达式的基本元素,不再包含其他表达式。

2、加号是非终结符,它表示一个表达式,需要通过计算5和6才能得出值。

所以简单来说:

1、终结符是表达式的基本元素,不再含其他表达式。

2、非终结符表示一个表达式,包含其他表达式。

二、UML类图

三、角色设计

角色描述
抽象表达式角色定义解释器的接口,声明一个解释操作interpret()
终结符表达式角色实现抽象表达式接口,表示语法规则中的终结符
非终结符表达式角色实现抽象表达式接口,表示语法规则中的非终结符,可以包含终结符和非终结符
客户端角色建立解释器对象,并调用interpret()方法进行解释操作

四、案例分析

下面我们通过模拟一个算术表达式的运算过程来实现解释器模式。

大体的工作流程如下:

1、构建表达式语法树。

2、用递归解释器对语法树进行解释执行。

3、终结符表达式直接返回值,非终结符表达式通过组合运算达到计算功能。

4、客户端构建语法树并调用解释器执行,即完成了对表达式的解析和求值。

代码设计如下:

定义一个抽象表达式角色,并定义了解释操作接口interpret():

public interface Expression {

    int interpret();

}

定义AddExpression,SubtractExpression等,非终结符表达式角色,它们实现了对复合表达式的解释,通过组合包含的子表达式的解释结果进行解释:

public class AddExpression implements Expression {
    private Expression left;
    private Expression right;

    public AddExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret() {
        return left.interpret() + right.interpret();
    }
}

public class SubtractExpression implements Expression {
    private Expression left;
    private Expression right;

    public SubtractExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret() {
        return left.interpret() - right.interpret();
    }
}

public class MultiplyExpression implements Expression {
    private Expression left;
    private Expression right;

    public MultiplyExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret() {
        return left.interpret() * right.interpret();
    }
}

public class DivideExpression implements Expression {
    private Expression left;
    private Expression right;

    public DivideExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret() {
        try {
            return left.interpret() / right.interpret();
        } catch (ArithmeticException e) {
            System.out.println("除数不能为0");
            return -1;
        }
    }
}

定义NumberExpression,终结符表达式角色,它实现了对终结符的解释,这里的终结符是数字,直接返回数字的值:

public class NumberExpression implements Expression {

    private int number;

    public NumberExpression(int number) {
        this.number = number;
    }

    @Override
    public int interpret() {
        return number;
    }
    
}

客户端角色,添加一个Interpreter类,来解析表达式字符串,构建抽象语法树,并调用interpret()方法:

public class Client {

    public static void main(String[] args) {
        Expression expression = new SubtractExpression(
                new AddExpression(new NumberExpression(5),
                        new MultiplyExpression(new NumberExpression(10), new NumberExpression(15))),
                new DivideExpression(new NumberExpression(5), new NumberExpression(5))
        );
        int result = expression.interpret();
        System.out.println("计算结果为:" + result);

    }
}

运行结果如下:

执行流程如下:

1、在客户端中,首先构建一个减法表达式SubtractExpression,它的左表达式是加法表达式AddExpression,右表达式是除法表达式DivideExpression

2、加法表达式AddExpression的左表达式是数字表达式NumberExpression(5),右表达式是乘法表达式MultiplyExpression

3、乘法表达式MultiplyExpression的左表达式是数字表达式NumberExpression(10),右表达式是数字表达式NumberExpression(15)

4、除法表达式DivideExpression的左表达式是数字表达式NumberExpression(5),右表达式是数字表达式NumberExpression(5)

5、这样就构建出了一个完整的表达式树。

6、然后调用这个减法表达式的interpret()方法进行解释执行。

7interpret()方法会递归调用子表达式的interpret()方法,终结于数字表达式的interpret()只返回数字的值。

8、这样整个表达式树就得到了解释执行,得到最终结果。

9、这里的执行顺序是:数字表达式->乘法表达式->加法表达式->除法表达式->减法表达式,逐步对表达式树进行解释,得到最终的值。

所以这个案例利用了解释器模式,定义了表达式接口,并为不同的表达式实现了interpret()方法,通过组合模式构建表达式树,然后递归解释执行以求值。 

五、总结

优点:

1、可扩展性好。新增解释表达式较为方便,符合开闭原则。

2、易于改变和扩展语法。比如增加新的运算符等。

3、将语法解释与执行分离,允许程序动态地定制,每个节点只需要处理自身逻辑。

4、各部分功能模块较为独立,有利于实现和维护。

5、使用递归解释方式简化了语法树的evaluate逻辑。

缺点

1、解释过程效率较低,递归调用开销较大。

2、类数量多,之间依赖关系复杂,导致系统较难理解。

3、语法树构建较为麻烦,需要客户端理解语法结构。

4、语法树的解析需要定义大量的类,不易管理。

5、语言扩展较难,需要更改多个表达式类。

6、错误处理不容易,需要增加额外处理。

7、调试过程较复杂。

应用场景:

1、语言解释器:用于解析和解释语言中的表达式,如编译器、解释器等。这个代码案例就展示了如何通过解释器模式解析算术表达式。

2、表达式求值:用于进行表达式求值,如正则表达式、数学表达式、逻辑表达式等的求值运算。

3、符号处理引擎:可用来进行数学符号、表达式的解析。

4、语法分析:可构建语法树,用来分析句子或表达式所遵循的语法规则。

5、业务规则解析:可定义业务规则语法,并进行解释执行,用来实现规则引擎。

6、格式转换器:可将一种格式转换为另一种格式。

符合的设计原则:

1、开闭原则(Open Closed Principle)

解释器模式中的解释器语法可以通过继承扩展,而无需修改原有解释器代码,符合开闭原则。

2、单一职责原则(Single Responsibility Principle)

解释器模式把语法解析的任务分发给不同的解释器类,每一个解释器只负责一小部分语法规则,符合单一职责原则。

3、组合复用原则(Composite Reuse Principle)

解释器模式中的解释器可以通过组合方式重用,符合组合复用原则。

4、接口隔离原则(Interface Segregation Principle)

解释器模式通过定义解释器接口将解析语法和解释执行分离,符合接口隔离原则。

5、依赖倒转原则(Dependence Inversion Principle)

解释器类都依赖一个抽象语法树而不是具体语法类,符合依赖倒转原则。

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

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

相关文章

Linux基础内容(25)—— 线程控制和线程结构

Linux基础内容(24) —— 线程概念_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/131294692?spm1001.2014.3001.5501 目录 1.线程操作 1.线程创建问题 2.线程终止问题 1.exit退出 2.pthread_exit退出 3.直接退出 3…

只需一个提示词解除GPT-4的字符限制!

ChatGPT的内存有限,GPT-3.5-turbo的限制为4897个令牌,而GPT-4的最大限制为8192。如果您在使用GPT-4进行聊天时超过8192个令牌(约6827个单词),它就会开始遗忘。我想出了一种新的技巧,可以轻松将对话扩展10倍。 这种技巧不会将对话中的每个字都保存到内存中。当您去开会时,会有人…

0502事务原理-InnoDB引擎-MySQL-数据库

1 概述 事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有操作作为一个整体一起向系统提交或者撤销操作请求,即这些操作要么同时成功,要么同时失败。 事务特性 原子性(Atomatic):事…

MiniGPT4系列之一部署篇:在RTX-3090 Ubuntu服务器部署步骤详解

MiniGPT4系列之一部署篇:在RTX-3090 Ubuntu服务器部署步骤详解_seaside2003的博客-CSDN博客 MiniGPT4系列之二推理篇命令行方式:在RTX-3090 Ubuntu服务器推理详解_seaside2003的博客-CSDN博客 MiniGPT4系列之三模型推理 (Web UI):在RTX-309…

外包软件定制开发中关于沟通障碍及对应解决方案

引言 外包软件定制开发在当今的商业环境中越来越常见。它为公司提供了许多好处,包括降低成本、加速交付和专注于核心业务。然而,沟通障碍常常是外包软件定制开发中的一个重要挑战。由于外包团队和客户位于不同的地理位置、文化和语言差异,沟…

Python 列表 sort()函数使用详解

「作者主页」:士别三日wyx 「作者简介」:CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」:小白零基础《Python入门到精通》 sort函数使用详解 1、升序降序2、sort()和sorted()的区别3、切片排序4、指定排序…

【C++】设计模式-单例模式

目录 一、单例模式 单例模式的三个要点 针对上述三要点的解决方案 常用的两类单例模式 二、懒汉模式实现 1.基本实现 2.锁静态成员析构单例 3.双层检查锁定优化 4.双层检查锁定智能指针 三、饿汉模式实现 1.基础实现 2.嵌套内部类解决内存泄漏 3.智能指针解决内存泄…

一种用于RBF神经网络的新型自适应内核研究(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

vuejs源码之虚拟dom中的vnode

在虚拟dom中,vnode是比较重要的。 什么是vnode 在vuejs中,有一个Vnode类 使用它可以实例不同类型的vnode实例,而不同类型的vnode实例各自表示不同类型的dom元素。 例如dom元素有文本节点,元素节点,注释节点等。 co…

Spring IoC及DI依赖注入

Spring 1.Spring的含义: Spring 可从狭义与广义两个角度看待 狭义的 Spring 是指 Spring 框架(Spring Fremework) 广义的 Spring 是指 Spring 生态体系 2.狭义的 Spring 框架 Spring 框架是企业开发复杂性的一站式解决方案 Spring 框架的核心是 IoC 容器和 AO…

数据库java中jdbcTemplate的事务问题

1.什么都不设置事务是默认提交的 两次获取的连接是不是一样的 参考文献(重磅): (542条消息) JdbcTemplate的事务控制_jdbctemplate transactionmanager_DayDayUp丶的博客-CSDN博客 PostMapping("/pinYin22")CrossOriginTransactionalpublic String pinYin22(HttpS…

【js实现语言国际化】使用json配置文件实现

需求:使用js让项目实现中文简体、繁体跟英文的切换,实现语言国际化 首先准备三种json配置文件: en.json {"textOne": "Today is Monday","textTwo": "Tomorrow is Tuesday","textThree"…

F#奇妙游(14):F#实现WPF的绑定

WPF中的绑定 绑定在UI开发中是一个非常重要的概念,它可以让我们的UI界面和数据模型之间建立起联系,当数据模型发生变化时,UI界面也会随之变化,反之亦然。这样的好处是显而易见的,我们不需要手动去更新UI界面&#xff…

金智教育IPO过会:计划募资约6亿元,郭超、史鸣杰为实控人

7月13日,深圳证券交易所披露的信息显示,江苏金智教育信息股份有限公司(下称“金智教育”)获得上市委会议通过。据贝多财经了解,金智教育于2022年6月30日递交上市申请材料,先后递交了6个版本的招股书&#x…

NDK OpenGL与OpenCV实现大眼萌特效

NDK​系列之OpenGL与OpenCV实现大眼萌特效,本节主要是在上一节OpenGL仿抖音极快极慢录制特效视频上增加大眼萌的特效。 OpenGL视频特效系列: NDK OpenGL渲染画面效果 NDK OpenGL离屏渲染与工程代码整合 NDK OpenGL仿抖音极快极慢录制特效视频 NDK O…

通讯录实现

普通版 需求 通讯录可以用来存储1000个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址 提供方法: 添加联系人信息删除指定联系人信息查找指定联系人信息修改指定联系人信息显示所有联系人信息清空所有联系人以名字排序所有联系…

【Linux后端服务器开发】UDP协议

目录 一、端口号 二、UDP报头格式 三、UDP的特点 四、UDP协议实现网络聊天群 一、端口号 端口号port标识了一个主机上进行通信的不同的应用程序。 0 ~ 1023:系统端口号,HTTP、FTP、SSH等这些广为使用的应用层协议,它们的端口号都是固定…

Windows软件开发常用技巧总结

本文总结了本人在日常工作学习中遇到的问题及其解决方法,没有固定的涉及领域 目的就是为了在下一次遇到类似问题的时候方便查找,从而快速解决问题 本文不定时更新~ 目录 Windows使用 如何实现桌面图标随意排列 文件资源管理器相关 显示隐藏文件 修改…

Linux--获取最近一次的进程退出码:echo $?

举例&#xff1a; #include <stdio.h> int main() { printf("hello world,pid: %d,ppid: %…

JavaFx 用户界面控件3——TableView

1.表格视图 TableView ableView是JavaFX提供的一个强大的控件&#xff0c;可以用于显示表格数据。它通过为TableView设定items属性&#xff08;存储行数据的ObservableList对象&#xff09;和列属性&#xff08;TableColumn对象&#xff09;来完成数据填充与展示。 以下是一个…