我们学习计算机程序语言,需要符合语法,是因为编译器会帮我们翻译我们的程序代码,那么Java的解释器模式就是一种解释文法的设计模式。
解释器模式定义:
给定一个语言,定义它的文法得一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
从上面的定义我们知道我们知道,首先就是要有一种文法,文法就是所谓的语法,像我们的汉语一样,一般请况之下我们要有主谓宾结构,比如:我 打 你,主语是: 我 ,谓语是一个动词: 打, 宾语是:你。
解释器无非就是解释符合文法的一个句子:
比如 “left move 10”
假设这是符合文法的一个句子,我们翻译成汉语就是 “向下 移动 10”
好了,为了更好的体现解释器设计模式,我们来用代码作为示例,翻译一下: left move 10 and up run 100;
代码:
首先我们要定义抽象的解释器:
/**
* 抽象的文法解释器
*/
public abstract class AbstractNode {
/**
* 解释方法
* @return 解释后的结果
*/
public abstract String interpret();
}
left 是一个方向词,那么我们也为他实现一个具体的文法解释器:
/**
* 方向文法的解释器
*/
public class DirectionNode extends AbstractNode {
private String direction; // 具体的方向
public DirectionNode(String direction) {
this.direction = direction;
}
/**
* 对向下左右的文法进行解释
* @return 文法解释后的结果
*/
@Override
public String interpret() {
if(direction.equalsIgnoreCase("up")) {
return "向上";
}else if(direction.equalsIgnoreCase("down")) {
return "向下";
}else if(direction.equalsIgnoreCase("left")) {
return "向左";
}else if(direction.equalsIgnoreCase("right")) {
return "向右";
}else {
return "无效指令";
}
}
}
move 是一个动词,我们也要为动词实现一个解释器:
/**
* 动作的文法
*/
public class ActionNode extends AbstractNode {
private String action; // 具体的动作,对具体的动作进行解释
public ActionNode(String action) {
this.action = action;
}
/**
* 如果动作是move 就返回移动 ,如果是run 就返回奔跑,否则就是无效的文法指令
* @return 解释后的结果
*/
@Override
public String interpret() {
if(action.equalsIgnoreCase("move")) {
return "移动";
}else if(action.equalsIgnoreCase("run")) {
return "奔跑";
}else {
return "无效指令";
}
}
}
10 是距离 ,接下来是一个距离的解释器:
/**
* 距离文法解释
*/
public class DistanceNode extends AbstractNode {
private String distance; // 具体的距离
public DistanceNode(String distance) {
this.distance = distance;
}
/**
* 解释文法中的距离
* @return 解释后的结果
*/
@Override
public String interpret() {
return distance + "米";
}
}
既然已经有 left move 10 ,很显然这是一个完整的短句了,那么我们就要有一个短句的解释器:
/**
* 句式解释器
*/
public class SentenceNode extends AbstractNode {
private AbstractNode direction; // 句式中的方向
private AbstractNode action; // 句式中的动作
private AbstractNode distance; // 句式中的距离
public SentenceNode(AbstractNode direction,AbstractNode action,AbstractNode distance) {
this.direction = direction;
this.action = action;
this.distance = distance;
}
/**
* 解释句式,句式一定要是 方向 + 动作 + 距离 如 : up move 10,如果不是就会出错
* @return 解释后的结果
*/
@Override
public String interpret() {
return this.direction.interpret() + this.action.interpret() + this.distance.interpret();
}
}
上面的短句解释器维护了 方向 动作 距离三种解释器来完成解释一个短句的语法
然后一个短句完成了,and 这一个连接词 连接了两个短句,那么我们要用一个and解释器来解释整个一个长句
/**
* and 连接词文法解释器
*/
public class AndNode extends AbstractNode {
private AbstractNode left; //左表达式
private AbstractNode right; //右表达式
AndNode(AbstractNode left,AbstractNode right){
this.left = left;
this.right = right;
}
/**
* 连接左右文法的解释
* @return 解释后的结果
*/
@Override
public String interpret() {
return this.left.interpret() + " 再 " + this.right.interpret();
}
}
这样,所有零件工具的解释器都有了
但是我们没有解释整个句子的总处理类,我们要定义怎么解释句式,就需要有一个句式处理器:
/**
* 句子解释处理类,也是上下文类
*/
public class InstructionHandler {
private AbstractNode node;
public void Handle(String instruction) {
//用栈来保存语法树。
AbstractNode left;
AbstractNode right;
AbstractNode direction;
AbstractNode action;
AbstractNode distance;
AbstractNode andNode;
Stack<AbstractNode> stack = new Stack<>();
//用空格分隔指令
String[] word = instruction.split(" ");
//循环
for(int i = 0; i < word.length; i++) {
if(word[i].equalsIgnoreCase("and")) { // 如果遇到连接词and,先将左解释器弹出,然后解释右半部,最后左右合成一个连接解释器
left = stack.pop();
String wordl = word[++i];
direction = new DirectionNode(wordl);
String word2 = word[++i];
action = new ActionNode(word2);
String word3 = word[++i];
distance = new DistanceNode(word3);
right = new SentenceNode(direction, action, distance);
andNode = new AndNode(left,right);
stack.push(andNode);
}else { // 如果没有遇到连接词and ,解释 方向 + 动作 + 距离,合成一个 短句解释器
String word1 = word[i];
direction = new DirectionNode(word1);
String word2 = word[++i];
action = new ActionNode(word2);
String word3 = word[++i];
distance = new DistanceNode(word3);
left= new SentenceNode(direction, action, distance);
stack.push(left);
}
}
this.node = stack.pop();
}
public String output() {
String result = node.interpret();
return result;
}
}
好了,接下来就是要测试了,上测试代码:
public class InterpreterTest {
public static void main(String[] args) {
String instruction ="down run 10 and left move 20 and up run 500";
InstructionHandler iHandler = new InstructionHandler();
iHandler.Handle(instruction);
String outString ;
outString =iHandler.output();
System.out.println(outString);
}
}
测试结果:
向下奔跑10米 再 向左移动20米 再 向上奔跑500米
上面我们使用翻译一个句子实例实现了一个Java解释器模式。想必大家也差不多能动解释器模式了
解释器模式的总结:
优点:
1. 易于改变和扩展文法,由于在解释器模式中使用类表示语言的文法规则,因此可以通过继承等机制来改变和扩张文法。
2. 每一条文法规则都可以表示为一个类,因此可以方便地实现每一个简单的语言。
3. 实现文法比较容易。
缺点:
1. 对于复杂文法难以维护,再解释模式下每一条规则至少定义一个类,因此如果太复杂无法处理。
2. 执行效率低,由于大量使用了循环和递归。因此再解释很复杂的句子时速度很慢
使用场景:
1. 可以将一个需要解释执行的语言中的句子表示为一个抽象树。
2. 一些重复出现的问题可以用简单的语言进行表达。
3. 执行效率不是关键问题。