尚硅谷设计模式学习(二十一)解释器模式
以四则运算问题引入解释器模式
先输入表达式的形式,比如 a+b+c-d+e,要求表达式的字母不能重复,在分别输入 a、b、c、d的值,最后求出结果。
传统方案
编写一个方法,接收表达式的形式,然后根据用户输入的数值进行解析,得到结果
问题分析
如果加入新的运算符,比如 * / 等等,不利于扩展,另外让一个方法来解析会造成程序结构混乱,
不够清晰。
一、解释器模式
1、基本介绍
在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器。
解释器模式(Interpreter Pattern):是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
应用场景
- 将一个需要解释执行的语言中的句子表示为一个抽象语法树
- 一些重复出现的问题可以用一种简单的语言来表达
- 编译器、运算表达式计算、正则表达式、机器人等
- Context:是环境角色,含有解释器之外的全局信息.
- AbstractExpression:抽象表达式, 声明一个抽象的解释操作,这个方法为抽象语法树中所有的节点所共享。
- TerminalExpression:为终结符表达式,实现与文法中的终结符相关的解释操作
- NonTermialExpression:为非终结符表达式,为文法中的非终结符实现解释操作
终结符和非终结符在计算机科学和语言学的领域是用来指定推导规则的元素。在某个形式语法之中,终结符和非终结符是两个不交的集合。
通俗的说,终结符不可再分,非终结符可再分,通过下面的例子会解释
2、代码实现
具体思路
加法解释器,减法解释器就是非终结符表达式,因为它们还能再分解成left,right
变量解释器就是终结符表达式
抽象类解释器Expression
public abstract class Expression {
/**
* 根据变量名称,返回对应值
* 例如: a + b ,key就是公式参数[a , b], value就是就是具体值
* HashMap {a=10, b=20}
*/
public abstract int interpret(HashMap<String,Integer> var);
}
变量解释器VarExpression
//变量解释器
public class VarExpression extends Expression {
private String key; //key = a , b
public VarExpression(String key) {
this.key = key;
}
//var就是{a=10, b=20}
//根据变量名称,返回对应值
@Override
public int interpret(HashMap<String, Integer> var) {
return var.get(this.key);
}
}
抽象运算符号解析器SymbolExpression,这里每个运算符号都只和自己左右两个数字有关系,
//抽象运算符号解析器,这里每个运算符号都只和自己左右两个数字有关系
public abstract class SymbolExpression extends Expression {
protected Expression left;
protected Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
//让其子类来实现
public abstract int interpret(HashMap<String,Integer> var);
}
加法解释器AddExpression
//加法解释器
public class AddExpression extends SymbolExpression {
public AddExpression(Expression left, Expression right) {
super(left, right);
}
///求left 和 right表达式相加后的结果
@Override
public int interpret(HashMap<String, Integer> var) {
return super.left.interpret(var) + super.right.interpret(var);
}
}
减法解释器SubExpression
public class SubExpression extends SymbolExpression {
public SubExpression(Expression left, Expression right) {
super(left, right);
}
//求出 left和 right表达式相减后的结果
@Override
public int interpret(HashMap<String, Integer> var) {
return super.left.interpret(var) - super.right.interpret(var);
}
}
计算器Calculator
public class Calculator {
//定义表达式
private Expression expression;
//构造函数传参并解析
public Calculator(String expStr){
//通过栈来进行运算操作
Stack<Expression> stack = new Stack();
//表达式拆分成字符数组
char[] charArray = expStr.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:
//如果是一个 Var 就创建 VarExpression 对象,并 push 到 stack
stack.push(new VarExpression(String.valueOf(charArray[i])));
}
}
this.expression = stack.pop();
}
public int run(HashMap<String,Integer> map){
return this.expression.interpret(map);
}
}
测试
public class Client {
public static void main(String[] args) throws IOException {
//获得表达式
String expStr = getExpStr();
//去掉空格
String str = expStr.replaceAll(" ", "");
//获得值映射
HashMap<String, Integer> map = getValue(str);
Calculator calculator = new Calculator(str);
//得到计算结果
int result = calculator.run(map);
System.out.println(expStr + " = " + result);
}
//获得表达式
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();
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;
}
}
结果
请输入表达式的值:a + b - c + d
请输入 a 的值:1
请输入 b 的值:2
请输入 c 的值:3
请输入 d 的值:4
a + b - c + d = 4
这里说明一下在Calculator构造器里计算完成后expression的值,这个expression是什么呢?
this.expression = stack.pop();
具体计算过程如图所示
得到expression后会执行run方法
public int run(HashMap<String,Integer> map){
return this.expression.interpret(map);
}
这里进行的是递归运算,debug一下这段代码会有更好的理解。
二、解释器模式的注意事项和细节
当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
使用解释器可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低。
key12315: 博主很用心,点赞一个,另外推荐一个在线utf-16编码解码的工具: https://config.net.cn/tools/utf16.html
骑着蜗牛去追星星: 一直loding 不是版本问题,而是需要启动使用了HystrixCommand命令的微服务
小鲁蛋儿: 【黑马满叔spring高级50讲!容器和Bean部分,深入原理,就业必备,这是你唯一认清spring的机会!-黑马程序员武汉中心-哔哩哔哩】 https://b23.tv/2wr7xGQ
小鲁蛋儿: 黑马程序员满老师的spring源码课
菜腿1994: 想问下博主参考的的书籍或者视频是哪些,感觉还挺深入的