解释器模式
定义
解释器模式(Interpreter Pattern)是一种按照规定语法进行解析的模式,现实项目中用得较少。
给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
优缺点、应用场景
优点
解释器是一个简单语法分析工具,最显著的特点就是拓展性。例如:添加一个运算符号的语法解释只需要添加一个类
缺点
- 引起类的膨胀。每个语法都要产生一个类,语法规则复杂时,可能产生大量类文件
- 采用递归调用,调试不便。
- 效率问题。由于使用了大量的循环和递归,当表达式过长且复杂时,可能出现效率问题。
应用场景
- 重复发生的问题。例如:多个应用服务器每天会产生大量的日志,数据要素相同但日志格式不同,这种情况就可以使用解释器模式,
- 一个简单语法需要解释的场景。期望使用一种(例如:符号)形式描述复杂逻辑,且类间还要进行递归调用的场景,可以考虑使用。
流程
- 输入一个表达式,包含值的占位符(a、b、c等)与运算符号(+、-)
- 对表达式字符串遍历,根据值的占位符数量要求用户输入对应个数的值,此时要确保每个占位符只能使用一次,不会出现循环赋值的情况
- 将值的占位符作为key,对应输入的值作为value,封装到map中
- 进入解释器模式的程序,开始运算
代码模拟场景
输入一个计算公式,并根据参数个数与运算符号,输出结果。
解释器模式
UML图
表达式抽象、实现
/**
* 表达式 抽象类
*/
public abstract class Expression {
/**
* 解析公式和数值
*
* @param map key是公式中的参数,value是参数对应的值
* @return 结果
*/
public abstract int interpreter(Map<String, Integer> map);
}
/**
* 变量解析器
*/
public class VarExpression extends Expression {
private final String key;
public VarExpression(String key) {
this.key = key;
}
@Override
public int interpreter(Map<String, Integer> map) {
return map.get(key);
}
}
/**
* 抽象运算符号解析器
*/
public abstract class SymbolExpression extends Expression {
protected Expression left;
protected Expression right;
/**
* 所有的解析公式都应只关心自己左右两个表达式的结果
*
* @param left 表达式左部
* @param right 表达式右部
*/
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
}
/**
* 加法解析器
*/
public class AddExpression extends SymbolExpression {
/**
* 所有的解析公式都应只关心自己左右两个表达式的结果
*
* @param left 表达式左部
* @param right 表达式右部
*/
public AddExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpreter(Map<String, Integer> map) {
return super.left.interpreter(map) + super.right.interpreter(map);
}
}
/**
* 减法解析器
*/
public class SubExpression extends SymbolExpression {
/**
* 所有的解析公式都应只关心自己左右两个表达式的结果
*
* @param left 表达式左部
* @param right 表达式右部
*/
public SubExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpreter(Map<String, Integer> map) {
return super.left.interpreter(map) - super.right.interpreter(map);
}
}
解释器封装类
/**
* 解释器的封装类
* 将运算符和运算符两边的值进行组合,实现解释
*/
public class Calculator {
private final Expression expression;
public Calculator(String expStr) {
Stack<Expression> stack = new Stack<>();
char[] cs = expStr.toCharArray();
Expression left;
Expression right;
for (int i = 0; i < cs.length; i++) {
switch (cs[i]) {
// 加法
case '+' -> {
left = stack.pop();
right = new VarExpression(String.valueOf(cs[++i]));
stack.push(new AddExpression(left, right));
}
// 减法
case '-' -> {
left = stack.pop();
right = new VarExpression(String.valueOf(cs[++i]));
stack.push(new SubExpression(left, right));
}
default -> stack.push(new VarExpression(String.valueOf(cs[i])));
}
}
this.expression = stack.pop();
}
public int run(Map<String, Integer> map) {
return this.expression.interpreter(map);
}
}
入口类
/**
* 解释器模式 入口类
*/
public class ExpressionMain {
public static void main(String[] args) {
String expStr = getExpStr();
Map<String, Integer> map = getValue(expStr);
Calculator calculator = new Calculator(expStr);
System.out.println("运算结果为:" + expStr + "=" + calculator.run(map));
}
/**
* 获取表达式
*
* @return 控制台输入的表达式
*/
private static String getExpStr() {
System.out.println("输入表达式:");
try {
return (new BufferedReader(new InputStreamReader(System.in))).readLine();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
/**
* 获得值映射
*/
public static Map<String, Integer> getValue(String expStr) {
Map<String, Integer> map = new HashMap<>();
char[] cs = expStr.toCharArray();
try {
for (char c : cs) {
// 如果是值的占位符
if (c != '+' && c != '-') {
// 解决重复参数的问题,确保值的占位符唯一
if (!map.containsKey(String.valueOf(c))) {
String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
map.put(String.valueOf(c), Integer.valueOf(in));
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return map;
}
}
结果
参考书籍
秦小波《设计模式之禅》