Demo 地址: https://github.com/ooblee/HelloDesignPattern
1. 定义
解释器模式(Interpreter Pattern):定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的“语言”是指使用规定格式和语法的代码。解释器模式是一种类行为型模式。
自定义语言,定义文法规则,并用文法规则解释语言中的句子。
具体实现的时候,会建立抽象语法树。
2. 设计
主要角色:
- 抽象表达式(Abstract Expression),声明了解释操作,所有表达式的父类。
- 终结符表达式(Terminal Expression),声明与终结符相关的解释操作。
- 非终结符表达式(Nonterminal Expression),声明与非终结符相关的解释操作。可以继续包含非终结符和终结符,进行递归或者循环处理。
- 上下文环境(Context),存储需要解释的语句,或者一些全局变量。
类图如下:
这里展示一个加减法表达式的实现。
抽象表达式,定义解释操作,返回操作后的值:
public interface IExpression {
int interpret();
}
加法表达式,属于非终止符表达式,解释的时候对左边和右边的表达式的结果进行相加:
public class PlusExpression implements IExpression {
private IExpression left;
private IExpression right;
public PlusExpression(IExpression left, IExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() + right.interpret();
}
}
减法表达式,属于非终止符表达式,解释的时候对左边和右边的表达式的结果进行相减:
public class MinusExpression implements IExpression {
private IExpression left;
private IExpression right;
public MinusExpression(IExpression left, IExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() - right.interpret();
}
}
数字表达式,属于终止符表达式,为整个抽象语法树的叶子节点,解释的时候把数字字符转为具体的数字:
public class NumberExpression implements IExpression {
public String number;
public NumberExpression(String number) {
this.number = number;
}
@Override
public int interpret() {
return Integer.valueOf(number);
}
}
表达式的处理类,如果复杂些的表达式,该处理类还会处理一些全局变量。这里没有这样的需求。处理待解释语句的时候,使用栈来构建抽象语法树。每次解析出来的表达式均入栈。如果非终止符表达式需要使用左边表达式的话,直接出栈弹出栈顶的表达式给非终止符表达式。
public class MathHandler {
public MathHandler() {
}
public int handle(String sequence) {
String[] words = sequence.split(" ");
Stack<IExpression> stack = new Stack<>();
for (int i = 0; i < words.length; i++) {
String word = words[i];
if (word.equalsIgnoreCase("+")) {
IExpression left = stack.pop();
IExpression right = new NumberExpression(words[++i]);
IExpression plus = new PlusExpression(left, right);
stack.push(plus);
} else if (word.equalsIgnoreCase("-")) {
IExpression left = stack.pop();
IExpression right = new NumberExpression(words[++i]);
IExpression minus = new MinusExpression(left, right);
stack.push(minus);
} else {
IExpression expression = new NumberExpression(word);
stack.push(expression);
}
}
IExpression expression = stack.pop();
return expression.interpret();
}
}
调用的方法,我们传入一些语句,看是否能够正确进行解释:
public class TestInterpret {
public static void main(String[] args) {
MathHandler mathHandler = new MathHandler();
String sequence = "1 + 1";
System.out.println(mathHandler.handle(sequence));
sequence = "1 + 2 + 3 + 4 + 5";
System.out.println(mathHandler.handle(sequence));
sequence = "1 + 2 + 3 - 4 - 5";
System.out.println(mathHandler.handle(sequence));
}
}
输出正常:
2
15
-3
3. 应用
适用场景:
- 对解释执行的语句抽象成语法树。
- 对重复出现的问题用简单的语句表达。
- 对执行效率要求不高。
3.1. JDK 的 Pattern
4. 特点
4.1. 优点
- 易修改和扩展,可以用继承的方式来对文法类进行扩展。
- 实现简单。每一个表达式节点的实现方式类似。
- 容易新增表达式。只需增加一个新的终结符表达式或非终结符表达式类。
4.2. 缺点
- 难以实现复杂文法。会导致类膨胀。
- 执行效率低。存在大量的循环和递归。