Java设计模式 - 解释器模式
😄 不断学习才是王道
🔥 继续踏上学习之路,学之分享笔记
👊 总有一天我也能像各位大佬一样
🏆 一个有梦有戏的人 @怒放吧德德
🌝分享学习心得,欢迎指正,大家一起学习成长!
简介
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
解释器模式原理分析
解释器模式就是给定一个表达式,定义他的文法表示,并且定义一个解释器,使用这个解释器来解释语言中的表达式。比较灵活,也比较容易拓展。
解释器模式比较不常见,一般应用于编译器、SQL解析、表达式计算。
原理UML图:
主要角色与职责介绍
Expression:解释器(表达式),声明一个抽象的解释操作,由具体的解释器继承实现,这个方法是抽象语法树中所有结点共享。
TerminalExpression:终结符表达式,实现文法中与终结符相关的解释操作
NoTerminalExpression:非终结符表达式,实现文法中与非终结符相关的解释操作
解释器模式实例
本次实验采用解释器模式来解决算数计算,输入运算表达式,通过具体的解释器模式去得出结果,本次只是使用了简单的加减法计算,如果需要扩展乘法除法、括号,就要考虑计算先后顺序,通过栈来实现。
本次类图:
①、定义抽象解释器
抽象表达式类,定义一个抽象方法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;
}
}
运行结果:
好,今天的分享就到这里。
👍创作不易,可能有些语言不是很通畅,如有错误请指正,感谢观看!记得一键三连哦!👍
根据以前编写的文章,慢慢查找不足点,一步一步进步,逐渐学习写出更好的文章。
💓德德小建议:
理解设计模式不是一件简单的事情,需要不断的学习和动手去练习,才能理解。只有掌握好设计模式,才能够真正的理解SpringAOP和Mybatis的底层原理。各位读者可以和我一样,动手敲一敲代码,甚至用不同的例子来做,通过debug一步一步调试,还有就是多看看别人的例子。能够有助于理解!谢谢各位观看指点!❤️ ❤️ ❤️