简介
今天给大家介绍的设计模式叫做“解释器模式”,该模式是“行为型设计模式”中的一员。
解释器模式的核心思想是:给定一个语言,定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子。
听完这句话话是不是顿时感觉一脸懵?什么语言、文法、句子,都是些什么鬼?
别慌让“菜鸟”来给你分析一波。
文法:可以将其理解为一种规则,就好比汉语中一句话必须由“主谓宾”三者构成一样。
句子:符合文法规则的一种表现,可以简单理解成符合汉语规则的一句话。
语言:将所有句子汇集起来的一个集合就是语言。
我们可以通过解释器将语言中的句子解析成一颗语法树,从而对其进行操作。所谓的语法树就是将句子通过文法进行推导,进而获取到句子的一棵树形表现形式。
解释器模式UML类图
类图讲解
Context:环境角色(上下文),含有每个解释器所需的一些数据或全局的一些信息。
AbstractExpression:抽象表达式类,声明了抽象的解释操作,所有解释器类都继承或实现该类。
TerminalExpression:终结符表达式类,是AbstractExpression的子类,实现了文法中有关终结符相关的解释操作。
TerminalExpression:非终结符表达式,AbstractExpression的子类,该类的功能与终结表达式类相反,文法中所有非终结符由该类进行解释。
Client:客户端测试类。
案例讲解
下面让我们通过一个经典的“计算器”案例来详细了解一下解释器模式。
注:本文重点讲解的是解释器模式故示例代码只实现了加减法的运算。
表达式接口
public interface Expression {
/**
* 解释表达式的抽象方法
*
* @param map 比如现有表达式:a + b;那么map中存放的就是{a=10,b=20}
* @return 返回解释后的值。
*/
int interpreter(Map<String, Integer> map);
}
变量解析器类
public class VarExpression implements Expression {
// 公式中的变量。
private String key;
public VarExpression(String key) {
this.key = key;
}
// 通过key获取所对应的值
@Override
public int interpreter(Map<String, Integer> map) {
return map.get(key);
}
}
运算符解析器类
public class SymbolExpression implements Expression {
/**
* 假如现有表达式:a + b - c 需要解析。
* 分析:
* 1、一个运算符连接的是它左右两个数字。
* 2、如上表达式【+】号连接的是吧“a"和"b",【-】号连接的是"a + b"和“c”。
* 3、经次分析我们将运算符连接的左右都看成是一个表达式也就是Expression。
*/
// 左表达式
protected Expression leftExpression;
// 右表达式
protected Expression rightExpression;
public SymbolExpression(Expression leftExpression, Expression rightExpression) {
this.leftExpression = leftExpression;
this.rightExpression = rightExpression;
}
// 不同种类的运算符由不同的运算符子类进行解析,所以该类不实现interpreter方法。
@Override
public int interpreter(Map<String, Integer> map) {
return 0;
}
}
减法解析器
public class SubExpression extends SymbolExpression {
public SubExpression(Expression leftExpression, Expression rightExpression) {
super(leftExpression, rightExpression);
}
// 解释减法
@Override
public int interpreter(Map<String, Integer> map) {
return leftExpression.interpreter(map) - rightExpression.interpreter(map);
}
}
加法解析器
public class AddExpression extends SymbolExpression {
public AddExpression(Expression leftExpression, Expression rightExpression) {
super(leftExpression, rightExpression);
}
// 解释加法
@Override
public int interpreter(Map<String, Integer> map) {
return leftExpression.interpreter(map) + rightExpression.interpreter(map);
}
}
计算器 => 对应Context角色
public class Calculator {
// 表达式
private Expression expression;
public Calculator(String strExpression) {
char[] charArray = strExpression.toCharArray();
// 定义栈用于存储表达式,因示例简单故不考虑运算顺序。
Stack<Expression> stack = new Stack<>();
Expression left;
Expression right;
// 解析表达式
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();
}
/**
*
* @param map 表达式对应的值
* @return 计算的结果
*/
public int calculate(Map<String, Integer> map) {
return this.expression.interpreter(map);
}
}
客户端测试类
public class Client {
public static void main(String[] args) {
// 表达式
String strExpression = "a+b-c+d";
// 表达式对应的值
Map<String, Integer> map = new HashMap<>();
map.put("a", 2);
map.put("b", 10);
map.put("c", 8);
map.put("d", 13);
// 创建计算器
Calculator calculator = new Calculator(strExpression);
// 计算
int result = calculator.calculate(map);
System.out.println("表达式:" + strExpression + "的计算结果为:" + result);
}
}
执行结果
总结
1、使用解释器模式可以提高代码的可扩展性。
2、使用解释器模式会引起类膨胀。
3、解释器模式会采用递归调用方法,可能会降低效率,并且会提高维护和调试的成本。
4、解释器模式的使用场景比较少,开发中也不经常接触到,一般常用在编译器、运算表达式计算、正则表达式解析等。
今天的分享就到这里了,如果感觉“菜鸟”写的文章还不错,记得点赞加关注呦!你们的支持就是我坚持下去的动力。文章哪里写的有问题的也希望大家可以指出,我会虚心受教。