【设计模式】Java设计模式 - 解释器模式

简介: 解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。

Java设计模式 - 解释器模式

😄 不断学习才是王道
🔥 继续踏上学习之路,学之分享笔记
👊 总有一天我也能像各位大佬一样
🏆 一个有梦有戏的人 @怒放吧德德
🌝分享学习心得,欢迎指正,大家一起学习成长!

java设计模式2.jpg

简介

解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。

解释器模式原理分析

解释器模式就是给定一个表达式,定义他的文法表示,并且定义一个解释器,使用这个解释器来解释语言中的表达式。比较灵活,也比较容易拓展。
解释器模式比较不常见,一般应用于编译器、SQL解析、表达式计算。
原理UML图:
解释器模式.jpg

主要角色与职责介绍

Expression:解释器(表达式),声明一个抽象的解释操作,由具体的解释器继承实现,这个方法是抽象语法树中所有结点共享。
TerminalExpression:终结符表达式,实现文法中与终结符相关的解释操作
NoTerminalExpression:非终结符表达式,实现文法中与非终结符相关的解释操作

解释器模式实例

本次实验采用解释器模式来解决算数计算,输入运算表达式,通过具体的解释器模式去得出结果,本次只是使用了简单的加减法计算,如果需要扩展乘法除法、括号,就要考虑计算先后顺序,通过栈来实现。
本次类图:
image.png

①、定义抽象解释器

抽象表达式类,定义一个抽象方法interpreter给子类(具体的解释器)来实现,目的是获取其值。

package com.lyd.demo.interpreter;
import java.util.HashMap;
/**
 * @Author: lyd
 * @Description: 抽象类表达式,通过HashMap 键值对, 可以获取到变量的值
 * @Date: 2022-09-10
 */
public abstract class Expression {
    /**
     * 解释公式和数值, key 就是公式(表达式) 参数[a,b,c], value就是具体值
     * @param var 存储数据
     * @return
     */
    public abstract int interpreter(HashMap<String, Integer> var);
}

②、具体的解释器

1)、变量解释器
key是表达式变量名,通过key来获取相应表达式中变量的值。

package com.lyd.demo.interpreter.var;
import com.lyd.demo.interpreter.Expression;
import java.util.HashMap;
/**
 * @Author: lyd
 * @Description: 变量解释器
 * @Date: 2022-09-10
 */
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); // 获取值
    }
}

2)、抽象运算符号解析器
因为四则运算有四种符号,每种符号有各自的操作,并且符号对数字的操作跟自己左右的数字有关系,需要对左右的变量的操作,并且这些都有一个共同父类-Expression,故在实例化运算符解释器的时候,需要传入左右的变量,并且赋值,在有具体的子类(具体运算符解释器)来做具体的操作,并且返回计算的值。

package com.lyd.demo.interpreter.symbol;
import com.lyd.demo.interpreter.Expression;
import java.util.HashMap;
/**
 * @Author: lyd
 * @Description: 抽象运算符号解析器 - 每个运算符号,都只和自己左右两个数字有关系,
 * @Date: 2022-09-10
 */
public class SymbolExpression extends Expression {
    public Expression left;
    public Expression right;
    public SymbolExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }
    // 因为有许多的符号,interpreter用子类在实现
    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return 0;
    }
}

2-1)、加法解释器
interpreter方法里面做具体的对应计算,传入的hashMap是变量值,从而获得计算的值。

package com.lyd.demo.interpreter.symbol;
import com.lyd.demo.interpreter.Expression;
import java.util.HashMap;
/**
 * @Author: lyd
 * @Description: 具体的运算符解释器 - 加法解释器
 * @Date: 2022-09-10
 */
public class AddExpression extends SymbolExpression{
    public AddExpression(Expression left, Expression right) {
        super(left, right);
    }
    /**
     * 子类才是真正的实现
     * @param var
     * @return
     */
    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return super.left.interpreter(var) + super.right.interpreter(var); // 真正的运算,左边 + 右边,返回其和
    }
}

2-2)、减法解释器

同加法解释器,只是把加号换成了减号。

package com.lyd.demo.interpreter.symbol;
import com.lyd.demo.interpreter.Expression;
import java.util.HashMap;
/**
 * @Author: lyd
 * @Description: 具体的运算符解释器 - 减法解释器
 * @Date: 2022-09-10
 */
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); // 父类的父类 - 根据具体的某个对象去获取值来计算
    }
}

③、计算器

根据表达式调用解释器来完成结果计算。计算四则运算,通过栈来实现,实现的是从左往右计算,但未涉及乘除、括号的优先级计算(读者可以自己进行扩展,主要扩展是实现运算符解释器以及通过栈做好优先级计算)。通过传来的字符串将其转成字符数组,通过遍历这些字符进行处理,遇到变量(数字)的时候,将其实例化成变量解释器存到栈中,在遇到符号的时候,通过获取上一个变量解释器,以及获取下一个数字并实例化成变量解释器,在实例化具体的运算符解释器并存入栈中,最后定义run方法执行解释器类的interpreter方法。

package com.lyd.demo.interpreter.calculator;
import com.lyd.demo.interpreter.Expression;
import com.lyd.demo.interpreter.symbol.AddExpression;
import com.lyd.demo.interpreter.symbol.SubExpression;
import com.lyd.demo.interpreter.var.VarExpression;
import java.util.HashMap;
import java.util.Stack;
/**
 * @Author: lyd
 * @Description: 计算器
 * @Date: 2022-09-10
 */
public class Calculator {
    // 定义表达式 - 抽象类表达式
    private Expression expression;
    // 构造函数传参,并解析
    public Calculator(String expStr) { // 传来的运算字符串
        // 用栈来实现计算顺序
        Stack<Expression> stack = new Stack<Expression>();
        // 表达式装成数组
        char[] chars = expStr.toCharArray();
        // 定义左右
        Expression left = null;
        Expression right = null;
        // 遍历,进行处理
        for (int i=0; i<chars.length; i++) {
            switch (chars[i]) {
                case '+' : // 加法运算
                    left = stack.pop(); // 取出值,并且在stack栈中去除(peek是瞄一下,只拿值,不删除)
                    right = new VarExpression(String.valueOf(chars[++i]));
                    stack.push(new AddExpression(left, right));// 然后根据得到left 和 right 构建 AddExpresson加入stack
                    break;
                case '-' : // 减法
                    left = stack.pop(); // 取出值,并且在stack栈中去除(peek是瞄一下,只拿值,不删除)
                    right = new VarExpression(String.valueOf(chars[++i]));
                    stack.push(new SubExpression(left, right));// 然后根据得到left 和 right 构建 AddExpresson加入stack
                    break;
                default:
                    // 如果是数值,直接存进去
                    stack.push(new VarExpression(String.valueOf(chars[i])));
                    break;
            }
        }
        // 便利完毕后,栈中会存放最后一个Expression
        this.expression = stack.pop();
    }
    public int run(HashMap<String, Integer> var) {
        // 传递给expression的interpreter进行解释执行
        return this.expression.interpreter(var);
    }
}

③、测试类

通过Calculator类去实现计算。getExpStr输入表达式,getValue是将传入的表达式进行拆分,在不是运算符的时候,将其变量字符作为map的key提示用户输入数字,存储表达式所对应的数值。

package com.lyd.demo.test;
import com.lyd.demo.interpreter.calculator.Calculator;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
/**
 * @Author: lyd
 * @Description: 测试类
 * @Date: 2022-09-10
 */
public class InterpreterTest {
    public static void main(String[] args) throws IOException {
        String expStr = getExpStr(); // a+b
        HashMap<String, Integer> var = getValue(expStr);// var {a=10, b=20}
        Calculator calculator = new Calculator(expStr);
        System.out.println("运算结果:" + expStr + "=" + calculator.run(var));
    }
    // 获得表达式
    public static String getExpStr() throws IOException {
        System.out.print("请输入表达式:");
        return (new BufferedReader(new InputStreamReader(System.in))).readLine();
    }
    // 获得值映射
    public static HashMap<String, Integer> getValue(String expStr) throws IOException {
        HashMap<String, Integer> map = new HashMap<String, Integer>();
        for (char ch : expStr.toCharArray()) {
            if (ch != '+' && ch != '-') {
                if (!map.containsKey(String.valueOf(ch))) {
                    System.out.print("请输入" + String.valueOf(ch) + "的值:");
                    String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
                    map.put(String.valueOf(ch), Integer.valueOf(in));
                }
            }
        }
        return map;
    }
}

运行结果:
image.png

好,今天的分享就到这里。

👍创作不易,可能有些语言不是很通畅,如有错误请指正,感谢观看!记得一键三连哦!👍

根据以前编写的文章,慢慢查找不足点,一步一步进步,逐渐学习写出更好的文章。

💓德德小建议:

理解设计模式不是一件简单的事情,需要不断的学习和动手去练习,才能理解。只有掌握好设计模式,才能够真正的理解SpringAOP和Mybatis的底层原理。各位读者可以和我一样,动手敲一敲代码,甚至用不同的例子来做,通过debug一步一步调试,还有就是多看看别人的例子。能够有助于理解!谢谢各位观看指点!❤️ ❤️ ❤️
相关文章
|
20天前
|
设计模式 算法 Java
Java中的设计模式:提升代码质量的秘诀
【8月更文挑战第23天】在Java开发中,设计模式是提高代码可读性、可维护性和扩展性的强有力工具。本文通过浅显易懂的语言和实际案例,探讨几种常见的设计模式及其在Java中的应用,旨在帮助开发者更好地理解并运用这些模式来优化自己的代码结构。
37 2
|
13天前
|
设计模式 缓存 算法
揭秘策略模式:如何用Java设计模式轻松切换算法?
【8月更文挑战第30天】设计模式是解决软件开发中特定问题的可重用方案。其中,策略模式是一种常用的行为型模式,允许在运行时选择算法行为。它通过定义一系列可互换的算法来封装具体的实现,使算法的变化与客户端分离。例如,在电商系统中,可以通过定义 `DiscountStrategy` 接口和多种折扣策略类(如 `FidelityDiscount`、`BulkDiscount` 和 `NoDiscount`),在运行时动态切换不同的折扣逻辑。这样,`ShoppingCart` 类无需关心具体折扣计算细节,只需设置不同的策略即可实现灵活的价格计算,符合开闭原则并提高代码的可维护性和扩展性。
30 2
|
13天前
|
设计模式 Java
Java 设计模式之谜:工厂模式与抽象工厂模式究竟隐藏着怎样的神奇力量?
【8月更文挑战第30天】在Java编程中,设计模式为常见问题提供了高效解决方案。工厂模式与抽象工厂模式是常用的对象创建型设计模式,能显著提升代码的灵活性、可维护性和可扩展性。工厂模式通过定义创建对象的接口让子类决定实例化哪个类;而抽象工厂模式则进一步提供了一个创建一系列相关或相互依赖对象的接口,无需指定具体类。这种方式使得系统更易于扩展和维护。
26 1
|
13天前
|
设计模式 Java
重构你的代码:探索Java中的混合、装饰器与组合设计模式
【8月更文挑战第30天】在软件开发中,设计模式为特定问题提供了结构化的解决方案,使代码更易理解、维护及扩展。本文将介绍三种常用的 Java 设计模式:混合模式、装饰器模式与组合模式,并附有示例代码展示实际应用。混合模式允许通过继承多个接口或抽象类实现多重继承;装饰器模式可在不改变对象结构的情况下动态添加新功能;组合模式则通过树形结构表示部分-整体层次,确保客户端处理单个对象与组合对象时具有一致性。
11 1
|
24天前
|
设计模式 算法 安全
Java编程中的设计模式:提升代码的可维护性和扩展性
【8月更文挑战第19天】在软件开发的世界里,设计模式是解决常见问题的一种优雅方式。本文将深入探讨Java编程语言中常用的几种设计模式,并解释如何通过这些模式来提高代码的可维护性和扩展性。文章不涉及具体的代码实现,而是侧重于理论和实践相结合的方式,为读者提供一种思考和改善现有项目的新视角。
|
18天前
|
设计模式 缓存 算法
探索 Java 设计模式及其重要性
【8月更文挑战第24天】
41 0
|
21天前
|
设计模式 SQL 缓存
Java编程中的设计模式:单例模式的深入理解与应用
【8月更文挑战第22天】 在Java的世界里,设计模式是构建可维护、可扩展和灵活的软件系统的基石。本文将深入浅出地探讨单例模式这一经典设计模式,揭示其背后的哲学思想,并通过实例演示如何在Java项目中有效运用。无论你是初学者还是资深开发者,这篇文章都将为你打开一扇洞悉软件设计深层逻辑的大门。
26 0
|
3月前
|
设计模式 缓存 安全
Java设计模式的单例模式应用场景
Java设计模式的单例模式应用场景
41 4
|
24天前
|
设计模式 存储 负载均衡
【五】设计模式~~~创建型模式~~~单例模式(Java)
文章详细介绍了单例模式(Singleton Pattern),这是一种确保一个类只有一个实例,并提供全局访问点的设计模式。文中通过Windows任务管理器的例子阐述了单例模式的动机,解释了如何通过私有构造函数、静态私有成员变量和公有静态方法实现单例模式。接着,通过负载均衡器的案例展示了单例模式的应用,并讨论了单例模式的优点、缺点以及适用场景。最后,文章还探讨了饿汉式和懒汉式单例的实现方式及其比较。
【五】设计模式~~~创建型模式~~~单例模式(Java)
|
4月前
|
设计模式 安全 Java
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式