三、设计模式
17.解释器模式(Interpreter Pattern)
17.1 定义
描述了如何为语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。这里的语言意思是使用规定格式和语法的代码。
17.2 角色
-
AbstractExpression表示抽象表达式,它声明一个抽象的解释操作,该接口为抽象语法树上所有的节点所共享;
-
TerminalExpression表示终结符表达式,它实现与文法中的终结符相关联的解释操作,语言中每一个句子的每个终结符都属于该类的一个实例;
-
NonterminalExpression表示非终结符表达式,它实现了文法中的非终结符的解释操作,在解释过程中一般需要应用递归的方式对句子进行处理;
-
Context表示上下文(环境),它包含了解释器之外一些其他的全局信息;
-
Client表示客户端,它用于构建表示该文法定义的语言中的一个特定的句子的抽象语法树,该语法树由终结符表达式(叶子节点)和非终结符表达式(中间节点)组成,并且Client负责调用解释操作。
17.3 结构图
17.4 代码实现
抽象接口
public interface AbstractNode {
public abstract String interpret();
}
实现类
class ActionNode implements AbstractNode
{
private String action;
public ActionNode(String action)
{
this.action = action;
}
public String interpret()
{
if(action.equalsIgnoreCase("move"))
{
return "移动";
}
else if(action.equalsIgnoreCase("run"))
{
return "快速移动";
}
else
{
return "无效指令";
}
}
}
class DirectionNode implements AbstractNode
{
private String direction;
public DirectionNode(String direction)
{
this.direction = direction;
}
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 "无效指令";
}
}
}
class DistanceNode implements AbstractNode
{
private String distance;
public DistanceNode(String distance)
{
this.distance = distance;
}
public String interpret()
{
return this.distance;
}
}
class SentenceNode implements 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;
}
public String interpret()
{
return direction.interpret() + action.interpret() + distance.interpret();
}
}
解释类
class AndNode implements AbstractNode
{
private AbstractNode left;
private AbstractNode right;
public AndNode(AbstractNode left,AbstractNode right)
{
this.left = left;
this.right = right;
}
public String interpret()
{
return left.interpret() + "再" + right.interpret();
}
}
工具
class InstructionHandler
{
private AbstractNode node;
public void handle(String instruction)
{
AbstractNode left=null,right=null;
AbstractNode direction=null,action=null,distance=null;
Stack stack=new Stack();
String[] words=instruction.split(" "); //以空格分隔字符串
for(int i=0;i<words.length;i++)
{
if(words[i].equalsIgnoreCase("and"))
{
left=(AbstractNode)stack.pop();
String word1=words[++i];
direction=new DirectionNode(word1);
String word2=words[++i];
action=new ActionNode(word2);
String word3=words[++i];
distance=new DistanceNode(word3);
right=new SentenceNode(direction,action,distance);
stack.push(new AndNode(left,right));
}
else
{
String word1=words[i];
direction=new DirectionNode(word1);
String word2=words[++i];
action=new ActionNode(word2);
String word3=words[++i];
distance=new DistanceNode(word3);
left=new SentenceNode(direction,action,distance);
stack.push(left);
}
}
this.node=(AbstractNode)stack.pop();
}
public String output()
{
String result = node.interpret();
return result;
}
}
测试
class Client
{
public static void main(String args[])
{
String instruction = "up move 5 and down run 10 and left move 5";
InstructionHandler handler = new InstructionHandler();
handler.handle(instruction);
String outString;
outString = handler.output();
System.out.println(outString);
}
}
输出
17.5 优点
- 解释器是一个简单语法分析工具,它最显著的优点就是扩展性,修改语法规则只要修改相应的非终结符表达式就可以了,若扩展语法,则只要增加非终结符类就可以了。
17.6 缺点
-
解释器模式会引起类膨胀。每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来了非常多的麻烦。
-
解释器模式采用递归调用方法。每个非终结符表达式只关心与自己有关的表达式,每个表达式需要知道最终的结果,必须一层一层的剥茧,无论是面向过程的语言还是面向对象的语言,递归都是在必要条件下使用的,它导致调试非常复杂,想想看,如果我们要排查一个语法错误,我们是不是要一个一个断点的调试下去直到最小的语法单元。
-
解释器模式由于使用了大量的循环和递归,效率是个不容忽视的问题,特别是用于解析复杂、冗长的语法时,效率是难以忍受的。