解释器模式
解释器模式用于对某种特定的语句进行分析和执行,当我们需要使用一些简单的语句来实现特定的操作的时候,只需要输入按照语言规则编写的语句,那么通过解释器模式可以按照预先定义的规则进行解析和执行。
定义
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解析语句中的句子。
结构
- AbstractExpression抽象表达式。终结符表达式和非终结符表达式的父类,定义了可以进行解释的方法。
- TerminalExpression终结符表达式。抽象表达式的子类,实现了终结符表达式的功能,与非终结符表达式联合实现特定功能。
- NonterminalExpression非终结符表达式。抽象表达式的子类,一般实现连接功能,可以包含终结符表达式和非终结符表达式,一般有递归功能。
- Context环境类。又称上下文类,存储需要解释的语句,关联了解释器,负责进行语句解析。
实例
做了一个实现加减乘除的例子,通过对输入的计算字符串进行分析并获取结果。但是感觉方案设计的不好,比如新增一个解释规则,需要新增一个文法规则类,并且修改大幅度Handler,不符合开闭原则。
package expression;
public interface Node {
int execute();
}
package expression;
public class Value implements Node {
private int number;
public Value(String number) {
this.number = Integer.valueOf(number);
}
@Override
public int execute() {
return this.number;
}
}
package expression;
public abstract class CalNode implements Node {
protected Node frontNode;
protected Node afterNode;
public CalNode(Node frontNode, Node afterNode) {
super();
this.frontNode = frontNode;
this.afterNode = afterNode;
}
}
package expression;
public class AddNode extends CalNode {
public AddNode(Node frontNode, Node afterNode) {
super(frontNode, afterNode);
}
@Override
public int execute() {
System.out.println("加运算" + frontNode.execute() + "+" + afterNode.execute());
return this.frontNode.execute() + this.afterNode.execute();
}
}
package expression;
public class DivNode extends CalNode {
public DivNode(Node frontNode, Node afterNode) {
super(frontNode, afterNode);
}
@Override
public int execute() {
System.out.println("除运算" + frontNode.execute() + "/" + afterNode.execute());
return this.frontNode.execute() / this.afterNode.execute();
}
}
package expression;
public class MulNode extends CalNode {
public MulNode(Node frontNode, Node afterNode) {
super(frontNode, afterNode);
}
@Override
public int execute() {
System.out.println("乘运算" + frontNode.execute() + "*" + afterNode.execute());
return this.frontNode.execute() * this.afterNode.execute();
}
}
package expression;
public class SubNode extends CalNode {
public SubNode(Node frontNode, Node afterNode) {
super(frontNode, afterNode);
}
@Override
public int execute() {
System.out.println("减运算" + frontNode.execute() + "-" + afterNode.execute());
return this.frontNode.execute() - this.afterNode.execute();
}
}
package expression;
import java.util.Objects;
import java.util.Stack;
public class Calculator {
private String context;
public String getContext() {
return context;
}
public void setContext(String context) {
this.context = context;
}
/**
* 计算加减乘除
*
* @return 计算结果
*/
public int calculate() {
String[] nodes = context.split(" ");
Node node = null;
Stack<Node> stack = new Stack<>();
for (int i = 1; i < nodes.length; i++) {
if (nodes[i].equals("*") || nodes[i].equals("/")) {
i++;
continue;
}
// 获取计算front变量
Node front = getFront(nodes, i, stack);
// 获取计算after变量
Node after = getAfter(nodes, i, stack);
// 获取具体计算对象
Node cal = getNode(front, after, nodes[i]);
stack.add(cal);
i++;
}
node = stack.pop();
return node.execute();
}
private Node getAfter(String[] nodes, int i, Stack<Node> stack) {
Stack<Node> mulDivStack = new Stack<>();
if (i + 2 < nodes.length && ((nodes[i + 2].equals("*") || nodes[i + 2].equals("/")))) {
return generateMulDivNode(nodes, i + 2, mulDivStack);
} else {
return new Value(nodes[i + 1]);
}
}
private Node generateMulDivNode(String[] nodes, int i, Stack<Node> mulDivStack) {
Node node = null;
Node front = mulDivStack.size() == 0 ? null : mulDivStack.pop();
front = front == null ? new Value(nodes[i - 1]) : front;
Node after = new Value(nodes[i + 1]);
if (Objects.equals("*", nodes[i])) {
node = new MulNode(front, after);
}
if (Objects.equals("/", nodes[i])) {
node = new DivNode(front, after);
}
if (i + 2 < nodes.length && ((nodes[i + 2].equals("*") || nodes[i + 2].equals("/")))) {
mulDivStack.add(node);
node = generateMulDivNode(nodes, i + 2, mulDivStack);
}
return node;
}
private Node getFront(String[] nodes, int i, Stack<Node> stack) {
if (i == 1 || stack.size() == 0) {
return new Value(nodes[i - 1]);
} else {
return stack.pop();
}
}
private Node getNode(Node front, Node after, String string) {
if (Objects.equals("+", string)) {
return new AddNode(front, after);
}
if (Objects.equals("-", string)) {
return new SubNode(front, after);
}
if (Objects.equals("*", string)) {
return new MulNode(front, after);
}
if (Objects.equals("/", string)) {
return new DivNode(front, after);
}
return null;
}
}
package expression;
public class Test {
public static void main(String[] args) throws Exception {
String context = "1 + 2 * 4 - 5 * 6";
Calculator cal = new Calculator();
cal.setContext(context);
System.out.println(cal.calculate());
}
}
优点
- 通过面向对象的思想对新定义的语言内容进行解析和执行,每一个节点都是一个对象,可以比较方便的实现一个简单的语言。
- 每种语言只需要新增一个相对应的解析规则,符合开闭原则。
适用场景
解释器模式在实际中应用较少,因为它维护了大量的类,维护起来十分麻烦,对于语法复杂的语言编写和维护的难度都很高,而且执行的过程中大多运用了递归,执行效率比较低,编写成本高。
在下面的场景中可以考虑使用解释器模式。
- 可以将一个需要解释执行的语言中的句子标识为一个抽象语法树。
- 一些重复出现的问题可以用一种简单的语言进行表达。
- 语言的文法比较简单。(复杂的文法可以考虑使用语法分析程序)
- 不要求较高的执行效率。