解释器模式
- 问题背景
- 解释器模式
- 基本介绍
- 原理类图
- 使用解释器模式来解决问题
- UML类图
- 代码示例
- 运行结果
- 注意事项和细节
问题背景
通过解释器模式来实现一个表达式的加减运算
例如:在我们先输入一个表达式:a+b+c-d+e,然后输入a,b,c,d,e的值,最终得到结果
解释器模式
基本介绍
解释器:
就是我们给一个特定的规则,然后我们来解释使用这个规则的输入,最终得到一个结果
解释器模式:
解释器模式就是给了我们一个框架,按照这个框架我们就能开发一个解释器
原理类图
1)AbstractExpression抽象类,是一个解释器抽象类,所有解释器都要继承它
2)TerminalExpression类,是终结表达式,就是就一个链路中,我们这个结点是知道结果的,不需要等待其他链路计算后才能得到结果,或者说我们这个节点是确定的。
3)NonTerminalExpression类,是非中介表达式,是在这个链路中我们这个表达式的结构是不确定的,需要依赖其他节点
4)Context类,是全局上下文,它保存了解释器之外的全局信息
使用解释器模式来解决问题
UML类图
1)Context是全局上下文,保存我们输入的表达式中字符对应的值
2)Expression是解释器抽象类
3)VarExpression是终结点解释器,解析表达式中的字符,例如:a+b,那就是解析a和b的数值是多少
4)SysmbolExpression是非终结点表达式,它用来解析左右表达式的运算
5)SubExpression是减法表达式,解析左右表达式相减的结果
6)AddExpression是加法表达式,解析左右表达式相加的结果
7)Calculator是计算器类,用来将表达式生成解析器链路
代码示例
/**
* 解释器抽象类
*/
public abstract class Expression {
/**
* 解析器必须要实现的解析方法
*
* @param context
* @return
*/
public abstract int interpreter(Context context);
}
/**
* 全局上下文
*/
public class Context {
private Map<String, Integer> map = new HashMap<>();
public Integer getValue(String key) {
return map.get(key);
}
public void setValue(String key, Integer value) {
map.put(key, value);
}
}
/**
* 终结表达式
*
* 解释表达式中的数值字符
*
* 例如:a = 10,就得到a的数值10
*/
public class VarExpression extends Expression{
private String key;
public VarExpression(String key) {
this.key = key;
}
@Override
public int interpreter(Context context) {
// 得到这个值对应的数值,返回
return context.getValue(this.key);
}
}
/**
* 非终结表达式
*
* 解释表达式中的运算符
*
* 对于加减运算,只能是对运算符左右两边的表达式链进行计算
*
*/
public abstract class SymbolExpression extends Expression{
/**
* 左表达式
*/
private Expression left;
/**
* 右表达式
*/
private Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
public Expression getLeft() {
return left;
}
public void setLeft(Expression left) {
this.left = left;
}
public Expression getRight() {
return right;
}
public void setRight(Expression right) {
this.right = right;
}
/**
* 交给具体运算符的子类来实现
*
* @param context
* @return
*/
@Override
public abstract int interpreter(Context context);
}
/**
* 减法
*/
public class SubExpression extends SymbolExpression{
public SubExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpreter(Context context) {
// 减法 左表达式减去右表达式
return super.getLeft().interpreter(context) - super.getRight().interpreter(context);
}
}
/**
* 加法
*
*/
public class AddExpression extends SymbolExpression{
public AddExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpreter(Context context) {
// 加法,所以就将左表达式和右表达式的值相加
return super.getLeft().interpreter(context) + super.getRight().interpreter(context);
}
}
/**
* 解析输入的表达式,生成解释器链路
*/
public class Calculator {
/**
* 生成解释器链路
*
* @param expStr
*/
public static Expression getExpression(String expStr) {
// 使用栈来安排解释器结点的先后顺序
Stack<Expression> stack = new Stack<>();
// 输入表达式转为字符数组
char[] charArray = expStr.toCharArray();
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;
}
}
return stack.pop();
}
}
public class Client {
public static void main(String[] args) throws IOException {
// 输入表达式
String expStr = getExpStr();
// 得到表达式符号对应的值
Context context = getValue(expStr);
// 生成解释器链路
Expression expression = Calculator.getExpression(expStr);
// 通过解释器计算结果
int result = expression.interpreter(context);
System.out.println("运算结果:" + expStr + "=" + result);
}
private static String getExpStr() throws IOException {
System.out.println("请输入表达式:");
return (new BufferedReader(new InputStreamReader(System.in))).readLine();
}
private static Context getValue(String expStr) throws IOException {
Context context = new Context();
for (char c : expStr.toCharArray()) {
if (c != '+' && c != '-') {
System.out.println("请输入字符"+c+"的值:");
String value = (new BufferedReader(new InputStreamReader(System.in))).readLine();
context.setValue(String.valueOf(c), Integer.valueOf(value));
}
}
return context;
}
}
运行结果
注意事项和细节
1)当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
2)应用场景:编译器、运算表达式计算、正则表达式、机器人等
3)使用解释器可能带来的问题: 解释器模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低