1.概述
解释器模式(Interpreter Pattern)是一种使用相对较少的模式,主要使用在编译解释等场景,例如:编译器、规则引擎解释、正则表达式解析等,这些语言又被称为领域特定语言(Domain Specific Language,DSL)。在《大话设计模式》中,对解释器的定义为:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。本文将详细分析解释器模式的原理及其使用。
2.原理及使用
2.1 原理
解释器模式UML类图如下所示:
类图中包含的核心角色有:抽象表达式(Abstract Expression)、终结符表达式(TerminalExpression)、非终结符表达式(NonTerminalExpression),还有的类图表达式中还有上下文(Context),也就是上述类图中的Client,负责传递数据给解释器。具体解释如下:
抽象表达式(AbstractExpression):定义解释器的接口(抽象类),内部定义了具体的解释方法,子类可实现该接口方法,实现具体的解释操作;
终结符表达式(TerminalExpression):抽象表达式的子类,实现与终结符相关的操作,每一个终结符应当都有一个具体终结表达式;
非终结符表达式(NonTerminalExpression):抽象表达式的子类,用来实现与非终结符相关的操作,每一条规则都应对应一个非终结符表达式;
上下文(Context):通常用于传递数据给各解释器,各解释器都能从此处获取数据。
2.2 案例
通过解释器模式实现四则运算,计算a+b-c的值,比如输入计算表达式形式为:a+b+c-d+e,要求表达式字母不能重复,最后求出结果。
案例类图如下:
上述类图中,AbstractExpression 是抽象表达式类,可以获取具体变量的值;SymbolExpression 是具体的符号运算符,其内部存储符号左右两侧对应的值;AddExpression 和SubExpression 是具体的解释器类型;VarExpression 是变量解析器,存储变量及其对应的值,根据变量获取对应的值。
编码如下:
/**
* 抽象类表达式,通过hashMap键值对,可以获取到变量的值
*/
public abstract class Expression {
//解释公式和数值,key就是公式(表达式),参数[a,b,c],value就是具体值
public abstract int interpreter(HashMap<String,Integer> var);
}
/**
* 符号运算符,每个运算符号,都只与自己左右的两个数字有关系
* 左右两个数字有可能是一个解析结果,无论何种类型,都是Expression的子类
*/
public class SymbolExpression extends Expression {
protected Expression left;
protected Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
/**
* symbolExpression由其子类实现
*
* @param var
* @return
*/
@Override
public int interpreter(HashMap<String, Integer> var) {
return 0;
}
}
/**
* 加法解释器
*/
public class AddExpression extends SymbolExpression {
public AddExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) + super.right.interpreter(var);
}
}
/**
* 减法解释器
*/
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);
}
}
/**
* 变量解析器,存储变量及其对应的值,根据变量获取对应的值
*/
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);
}
}
public class Calculator {
private Expression expression;
public Calculator(String expression) {
Stack<Expression> stack = new Stack<>();
char[] charArray = expression.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:
stack.push(new VarExpression(String.valueOf(charArray[i])));
break;
}
}
this.expression = stack.pop();
}
public int run(HashMap<String, Integer> var) {
return this.expression.interpreter(var);
}
}
//测试类
public class Client {
public static void main(String[] args) throws IOException {
String expression = getExpStr();
HashMap<String, Integer> var = getValues(expression);
Calculator calculator = new Calculator(expression);
System.out.println("运算表达式:" + expression + "=" + calculator.run(var));
}
public static String getExpStr() throws IOException {
System.out.println("请输入表达式:");
return (new BufferedReader(new InputStreamReader(System.in)).readLine());
}
public static HashMap<String, Integer> getValues(String expr) throws IOException {
HashMap<String, Integer> map = new HashMap<>();
for (char ch : expr.toCharArray()) {
if (ch != '+' && ch != '-') {
if (!map.containsKey(String.valueOf(ch))) {
System.out.println("请输入" + ch + "的值:");
String value = new BufferedReader(new InputStreamReader(System.in)).readLine();
map.put(String.valueOf(ch), Integer.valueOf(value));
}
}
}
return map;
}
}
运行结果如下:
2.3 注意事项
1.解释器的使用主要在底层框架中体现,主要用于一些语法及词法解释,让程序具有更好地扩展性;
2.解释器使用的场景包括:编译器、正则表达式、运算符表达式等;
3.使用解释器在一定程度上可能造成类膨胀,同时可能采用递归调用方式,导致调试非常复杂,效率较低。
3.小结
1.解释器在日常开发中使用相对较少,主要用于底层框架中;
2.解释器在一定程度上让程序具有更好地扩展性。
4.参考文献
1.《设计模式之禅》-秦小波著
2.《大话设计模式》-程杰著
3.https://www.bilibili.com/video/BV1G4411c7N4-尚硅谷设计模式
4.https://www.runoob.com/design-pattern/observer-pattern.html