小侃设计模式(十九)-解释器模式

news2024/9/24 17:13:30

1.概述

解释器模式(Interpreter Pattern)是一种使用相对较少的模式,主要使用在编译解释等场景,例如:编译器、规则引擎解释、正则表达式解析等,这些语言又被称为领域特定语言(Domain Specific Language,DSL)。在《大话设计模式》中,对解释器的定义为:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。本文将详细分析解释器模式的原理及其使用。

2.原理及使用

2.1 原理

解释器模式UML类图如下所示:
在这里插入图片描述

类图中包含的核心角色有:抽象表达式(Abstract Expression)、终结符表达式(TerminalExpression)、非终结符表达式(NonTerminalExpression),还有的类图表达式中还有上下文(Context),也就是上述类图中的Client,负责传递数据给解释器。具体解释如下:

抽象表达式(AbstractExpression):定义解释器的接口(抽象类),内部定义了具体的解释方法,子类可实现该接口方法,实现具体的解释操作;
终结符表达式(TerminalExpression):抽象表达式的子类,实现与终结符相关的操作,每一个终结符应当都有一个具体终结表达式;
非终结符表达式(NonTerminalExpression):抽象表达式的子类,用来实现与非终结符相关的操作,每一条规则都应对应一个非终结符表达式;
上下文(Context):通常用于传递数据给各解释器,各解释器都能从此处获取数据。

2.2 案例

通过解释器模式实现四则运算,计算a+b-c的值,比如输入计算表达式形式为:a+b+c-d+e,要求表达式字母不能重复,最后求出结果。
案例类图如下:
在这里插入图片描述
上述类图中,AbstractExpression 是抽象表达式类,可以获取具体变量的值;SymbolExpression 是具体的符号运算符,其内部存储符号左右两侧对应的值;AddExpression 和SubExpression 是具体的解释器类型;VarExpression 是变量解析器,存储变量及其对应的值,根据变量获取对应的值。

编码如下:

/**
 * 抽象类表达式,通过hashMap键值对,可以获取到变量的值
 */
public abstract class Expression {

    //解释公式和数值,key就是公式(表达式),参数[a,b,c],value就是具体值
    public abstract int interpreter(HashMap<String,Integer> var);

}

/**
 * 符号运算符,每个运算符号,都只与自己左右的两个数字有关系
 * 左右两个数字有可能是一个解析结果,无论何种类型,都是Expression的子类
 */
public class SymbolExpression extends Expression {

    protected Expression left;

    protected Expression right;

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

    /**
     * symbolExpression由其子类实现
     *
     * @param var
     * @return
     */
    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return 0;
    }
}

/**
 * 加法解释器
 */
public class AddExpression extends SymbolExpression {

    public AddExpression(Expression left, Expression right) {
        super(left, right);
    }

    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return super.left.interpreter(var) + super.right.interpreter(var);
    }
}

/**
 * 减法解释器
 */
public class SubExpression extends SymbolExpression {

    public SubExpression(Expression left, Expression right) {
        super(left, right);
    }

    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return super.left.interpreter(var) - super.right.interpreter(var);
    }
}


/**
 * 变量解析器,存储变量及其对应的值,根据变量获取对应的值
 */
public class VarExpression extends Expression {

    private String key;

    public VarExpression(String key) {
        this.key = key;
    }

    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return var.get(this.key);
    }
}

public class Calculator {

    private Expression expression;

    public Calculator(String expression) {
        Stack<Expression> stack = new Stack<>();
        char[] charArray = expression.toCharArray();
        Expression left = null;
        Expression right = null;
        /**循环遍历字符串对应的值,计算符号左右两侧结果**/
        for (int i = 0; i < charArray.length; i++) {
            switch (charArray[i]) {
                case '+':
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new AddExpression(left, right));
                    break;
                case '-':
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new SubExpression(left, right));
                    break;
                default:
                    stack.push(new VarExpression(String.valueOf(charArray[i])));
                    break;
            }
        }
        this.expression = stack.pop();
    }


    public int run(HashMap<String, Integer> var) {
        return this.expression.interpreter(var);
    }

}

//测试类
public class Client {

    public static void main(String[] args) throws IOException {
        String expression = getExpStr();
        HashMap<String, Integer> var = getValues(expression);
        Calculator calculator = new Calculator(expression);
        System.out.println("运算表达式:" + expression + "=" + calculator.run(var));
    }

    public static String getExpStr() throws IOException {
        System.out.println("请输入表达式:");
        return (new BufferedReader(new InputStreamReader(System.in)).readLine());
    }

    public static HashMap<String, Integer> getValues(String expr) throws IOException {
        HashMap<String, Integer> map = new HashMap<>();
        for (char ch : expr.toCharArray()) {
            if (ch != '+' && ch != '-') {
                if (!map.containsKey(String.valueOf(ch))) {
                    System.out.println("请输入" + ch + "的值:");
                    String value = new BufferedReader(new InputStreamReader(System.in)).readLine();
                    map.put(String.valueOf(ch), Integer.valueOf(value));
                }
            }
        }
        return map;
    }
}

运行结果如下:
在这里插入图片描述

2.3 注意事项

1.解释器的使用主要在底层框架中体现,主要用于一些语法及词法解释,让程序具有更好地扩展性;
2.解释器使用的场景包括:编译器、正则表达式、运算符表达式等;
3.使用解释器在一定程度上可能造成类膨胀,同时可能采用递归调用方式,导致调试非常复杂,效率较低。

3.小结

1.解释器在日常开发中使用相对较少,主要用于底层框架中;
2.解释器在一定程度上让程序具有更好地扩展性。

4.参考文献

1.《设计模式之禅》-秦小波著
2.《大话设计模式》-程杰著
3.https://www.bilibili.com/video/BV1G4411c7N4-尚硅谷设计模式
4.https://www.runoob.com/design-pattern/observer-pattern.html

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

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

相关文章

【数据结构与算法】数据结构介绍与稀疏数组相关操作

文章目录一 数据结构介绍1 数据结构和算法的重要性2 数据结构和算法的关系3 线性结构和非线性结构&#xff08;1&#xff09;线性结构&#xff08;2&#xff09;非线性结构二 稀疏数组1 应用场景2 基本概念&#xff08;1&#xff09;基本介绍&#xff08;2&#xff09;压缩策略…

并发编程——1.进程与线程

目录1.进程与线程1.1.概述1.2.并行与并发本文笔记整理来自黑马视频https://www.bilibili.com/video/BV16J411h7Rd/?p1&#xff0c;相关资料可在视频评论区进行获取。 1.进程与线程 1.1.概述 &#xff08;1&#xff09;进程 程序由指令和数据组成&#xff0c;但这些指令要运…

2022 年度作品优秀大赏 | 开发者说·DTalk

岁聿云暮之际&#xff0c;回首 2022&#xff0c;开发者们一直在迈着坚定的步伐向顶峰攀登&#xff0c;我们也竭尽所能不断为大家提供帮助——操作系统 Android 13 完成 Beta 版到正式版的蜕变&#xff0c;开发工具 Flutter 3.3 全力支持您的跨平台之旅&#xff0c;应用平台 Goo…

并行、并发、同步、异步、阻塞、非阻塞

并行 是指两个或者多个事件在同一时刻发生。并行是在不同实体上的多个事件。 并行针对多核 CPU 而言&#xff0c;它指的是多个核心同时执行多个任务的能力 单核 CPU 无法并行&#xff0c;并行只可能发生在多核 CPU 中。 并发 是指两个或多个事件在同一时间间隔发生。并发是…

【观察】让行业AI“触手可及”,NVIDIA创新与实践“从未止步”

毫无疑问&#xff0c;今天AI正与产业结合得越来越紧密&#xff0c;从自动驾驶&#xff0c;到智慧医疗&#xff0c;智慧金融、智慧城市等&#xff0c;AI已经开始渗透到我们生活的方方面面。事实上&#xff0c;即便目前来自传统行业用户的AI转型需求尚未完全激活爆发&#xff0c;…

第一篇 AlexNet——论文翻译

文章目录摘要1 简介2 数据集3 架构 3.1 ReLU非线性3.2 多GPU训练3.3 局部响应归一化3.4 重叠池化3.5 整体架构4 减少过拟合4.1 数据增强4.2 失活(Dropout)5 学习细节6 结果6.1 定性评估7 探讨摘要 论文链接&#xff1a;http://www.cs.toronto.edu/~fritz/absps/imagenet.pdf …

VTK--交互方式vtkInteractorStyleTrackballCamera

前言&#xff1a;本博文主要介绍vtk中基于Camera的交互方式vtkInteractorStyleTrackballCamera 及其子类&#xff0c;小伙伴可以根据需求自定义交互方式。 目录 vtkInteractorStyleTrackballCamera vtkGeoInteractorStyle vtkInteractorStyleImage vtkInteractorStyleMult…

LeetCode刷题复盘笔记—一文搞懂动态规划之152. 乘积最大子数组问题(动态规划系列第三十六篇)

今日主要总结一下动态规划的一道题目&#xff0c;152. 乘积最大子数组 题目&#xff1a;152. 乘积最大子数组 Leetcode题目地址 题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;请你找出数组中乘积最大的非空连续子数组&#xff08;该子数组中至少包含一个数字&…

怎样学习线性代数?

最近在看《线性代数的几何意义》这本书&#xff0c;刚好也借用书里的总结&#xff0c;分享一下。 &#xff08;注&#xff1a;本文是一篇我国代数名家丘维声教授在电大讲授线性代数课程时关于如何学好线性代数的综合论述&#xff0c;超牛&#xff01;&#xff09; 初学线性代数…

社区10款年度优秀插件框架盘点!

Cocos 社区年度插件框架 TOP 10以下游戏资源排名不分先后&#xff0c;Cocos Store & Cocos微店 年终元旦限时优惠进行中......作者&#xff1a;Chuan——张川介绍&#xff1a;一款节点树实时预览插件&#xff0c;支持 Cocos Creator 2.x\3.x 引擎版本&#xff0c;除节点预览…

云信小课堂 | 如何管理音视频的通话状态?

2022.12业务背景 用户在实际使用云信音视频通话 2.0 服务中&#xff0c;常常会遇到弱网&#xff0c;掉线等情况&#xff0c;对于这类情况&#xff0c;云信 SDK 提供了自动重连的相关策略&#xff0c;业务层不需要单独处理重连&#xff0c;只需要监听并做好相应的 UI 逻辑。所以…

【OpenCV】使用 Python 的铅笔素描图像

目录&#xff1a;使用 Python 的铅笔素描图像一、前言二、代码实战2.1 导包2.2 读取照片2.3 使用 OpenCV 显示图像2.4 灰度图像2.5 反转图像2.6 模糊图像2.7 减淡和融合三、结果展示五、源代码一、前言 图片在 Python 中表示为一组数字。所以我们可以进行各种矩阵操作来得到令…

跨域问题(三种解决方法)

跨域就是一个域名的网页去请求另一个域名的资源&#xff0c;比如你刚刚在A网站输入了自己的账号密码&#xff0c;然后访问B网站&#xff0c;B网站无法获取账号密码 两个请求的协议&#xff08;比如http&#xff09;&#xff0c;域名&#xff08;比如说localhost或者192.168.0.…

python 时间

目录标题python的时间模块1、时间戳2、时间元组3、获取格式化的时间可以自定义输出格式日期格式化的符号4、显示某月的日历5、sleep模块python的时间模块 1、时间戳 时间戳&#xff0c;以1970为时间基准&#xff0c;但是太过于遥远的时间就不可以了&#xff0c;windows最源支持…

基于springboot实验室管理系统(程序+数据库)

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…

C进阶:征服指针之指针笔试题强化(3)

目录 ​​​​​​​ &#x1f432;T1. ​​​​​​​ ​​​​​​​ &#x1f916; T2. ​​​​​​​ ​​​​​​​ &#x1f42c; T3. ​​​​​​​ ​​​​​​​ &#x1f433; T4. ​​​​​​​ ​​​…

并发编程——5.共享模型之无锁

目录5.共享模型之无锁5.1.问题提出5.1.1.案例5.1.2.解决思路——锁5.1.3.解决思路——无锁5.2.CAS 与 volatile5.2.1.CAS5.2.2.volatile5.2.3.为什么无锁效率高5.2.4.CAS 的特点5.3.原子整数5.4.原子引用5.4.1.概述5.4.2.案例5.4.3.ABA 问题及解决5.5.原子数组5.6.字段更新器5.…

数位统计DP

目录 算法简介 算法讲解 数字计数 数位统计DP的递推实现 数位统计DP的记忆化搜索实现 算法实践 一 Windy数 二 手机号码 附录&#xff1a; 算法简介 数位统计 DP 用于数字的数位统计,是一种比较简单的 DP 套路题。 一个数字的数位有个位、十位、百位,等等,如果题目和…

Python3入门基础(03)数据结构

Python3 数据结构 Python3 中有四种标准的数据结构&#xff1a; List&#xff08;列表&#xff09;Tuple&#xff08;元组&#xff09;Set&#xff08;集合&#xff09;Dictionary&#xff08;字典&#xff09; Python 3 的六个标准数据类型中&#xff1a; 不可变数据&…

MATLAB算法实战应用案例精讲-【人工智能】语义分割

前言 语义分割是一种典型的计算机视觉问题,其涉及将一些原始数据(例如,平面图像)作为输入并将它们转换为具有突出显示的感兴趣区域的掩模。许多人使用术语全像素语义分割(full-pixel semantic segmentation),其中图像中的每个像素根据其所属的感兴趣对象被分配类别ID。…