设计模式学习(四)——解释器模式

解释器模式定义

解释器模式(Interpreter Pattern)描述了如何为语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。这里的语言意思是使用规定格式和语法的代码。

简单说来,解释器模式用来为语言创建解释器。

如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子,这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题,而且当文法简单、效率不是关键问题的时候效果最好。

解释器模式将一个语法规则表示成一个类,便于实现。而且,因为语法由许多类表示,所以我们可以轻易地改变和扩展此语言。


解释器模式结构图

图中:

  • AbstractExpression表示抽象表达式,它声明一个抽象的解释操作,该接口为抽象语法树上所有的节点所共享;

  • TerminalExpression表示终结符表达式,它实现与文法中的终结符相关联的解释操作,语言中每一个句子的每个终结符都属于该类的一个实例;

  • NonterminalExpression表示非终结符表达式,它实现了文法中的非终结符的解释操作,在解释过程中一般需要应用递归的方式对句子进行处理;

  • Context表示上下文(环境),它包含了解释器之外一些其他的全局信息;

  • Client表示客户端,它用于构建表示该文法定义的语言中的一个特定的句子的抽象语法树,该语法树由终结符表达式(叶子节点)和非终结符表达式(中间节点)组成,并且Client负责调用解释操作。


解释器模式实例——机器人控制程序

实例说明

某机器人控制程序包含一些简单的英文指令,其文法规则如下:

expression : : = direction action distance | composite

composite : : = expression ‘and’ expression

direction : : = ‘up’ | ‘down’ | ‘left’ | ‘right’

action : : = ‘move’ | ‘run’

distance : : = an integer //一个整数值

如输入:up move 5,则输出“向上移动5个单位”;输入:down run 10 and left move 20,则输出“向下快速移动10个单位再向左移动20个单位”。

现使用解释器模式来设计该程序并模拟实现。

实例类图

实例类图中与解释器模式结构图对应的类主要有:

  • AbstractNode充当抽象表达式角色;

  • DirectionNode、ActionNode和DistanceNode充当终结符表达式角色;

  • AndNode和SentenceNode充当非终结符表达式角色;

  • InstructionHandler为指令处理类,充当工具类角色。

实例代码

AbstractNode.java:

//抽象表达式
abstract class AbstractNode
{
public abstract String interpret();
}

DirectionNode.java:

//方向解释:终结符表达式
class DirectionNode extends 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 "无效指令";
}
}
}

ActionNode.java:

//动作解释:终结符表达式
class ActionNode extends 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 "无效指令";
}
}
}

DistanceNode.java:

//距离解释:终结符表达式
class DistanceNode extends AbstractNode
{

private String distance;

public DistanceNode(String distance)
{
this.distance = distance;
}

public String interpret()
{
return this.distance;
}
}

AndNode.java:

//And解释:非终结符表达式
class AndNode extends 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();
}
}

SentenceNode.java:

//简单句子解释:非终结符表达式
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;
}
public String interpret()
{
return direction.interpret() + action.interpret() + distance.interpret();
}
}

InstructionHandler.java:

import java.util.*;

//指令处理类:工具类
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;
}
}

Client.java:

//客户端测试类
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);
}
}

运行结果

附加描述

在本实例中,我们将一个DirectionNode(方向节点)、一个ActionNode(动作节点)和一个DistanceNode(距离节点)组成一个SentenceNode(句子节点)。句子节点再通过and连接在一起,形成更加复杂的结构。本实例的工具类InstructionHandler用于对输入指令进行处理,将输入指令分割为字符串数组,将第1个、第2个和第3个单词组合成一个句子,并存入栈中;如果发现有单词and,则先将原先栈中所存句子取出来作为and的左表达式,再将and后的第1个、第2个和第3个单词组合成一个句子作为and的右表达式,然后将组合而成的AndNode节点存入栈中。以此类推,直到整个指令解析结束。

我们可以通过抽象语法树来表示解析过程,如指令 down run 10 and left move 20 对应的抽象语法树如下图所示。


后话

通过学习解释器模式,让我对树结构的用法有了进一步的理解。