定义
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示解释语言中的句子。
像Java C++等语言没有办法直接的解释语句,必须定义一套自己的文法规则来实现对语句的解释。解释器描述了如何如何为简单的语言定义一个文法,如何在该语言中表示一个句子,如何解释句子,如何构造一个抽象的语法树。
(如果在学习大学课程编译原理之后,学习解释器模式就会更加的容易一些)
结构
Context是环境类也叫作上下文类,用存储解释器之外的一些全局信息,通常用它来临时存储需要解释的语句。
AbstractExpression抽象表达式类,所有终结符表达式和非终结符表达式的公共父类,声明了抽象的解释操作(interpret()方法)。
TerminalExpression终结符表达式类。
NonterminalExpression非终结符表达式类。
在解释器模式中,每一种终结符和非终结符都有一个具体的实现类,使用类表示每一条文法规则,所以系统具有良好的灵活性和可扩展性。
实现代码:
public abstract class AbstractExpression{
public abstract void interpret(Context context);
}
public class TerminalExpression extends AbstractExpression{
public abstract void interpret(Context context){
//终结符表达式的解释操作
}
}
public class NonterminalExpression extends AbstractExpression{
private AbstractExpression left;
private AbstractExpression right;
public NonterminalExpression(AbstractExpression left,AbstractExpression right){
this.left = left;
this.right = right;
public abstract void interpret(Context context){
//非终结符表达式的解释操作
}
}
public class Context{
private HashMap<String,String> map = new HashMap<>();
public void assign(String key,String value){
map.put(key,value);//往环境中设值
}
public String lookup(String key){
return map.get(key);
}
}
实例
某公司要开发一个机器人的控制程序,包含一些简单的英文控制指令,每一个指令对应一个表达式,一个简单的表达式由移动方向、移动方式、移动距离表示。表达式可以为一个简单的表达式也可以是一个复杂的表达式。
移动方向:up down left right
移动方式:move run
移动距离:int整数
两个简单表达式可以由and连接成一个复合表达式。
expression::=direction action distance | composite
composite::=expression 'and’expression
direction::= ‘up’ | ‘down’ | ‘left’ | ‘right’ |
action::= ‘move’ | ‘run’ |
distance::= an integer
针对以上五条文法规则,使用五个类来实现,其中终结符表达式 direction action 和 distance对应DirectionNode ActionNode Distance 非终结符对应 SentenceNode 和 AndNode
以下是机器人控制程序的抽象语法树
direction = new DirectionNode(dir);
String a = words[++i];
action = new ActionNode(a);
String dis = words[++i];
distance = new DistanceNode(dis);
package interpreter;
public class AndNode extends AbstractNode {
private AbstractNode left;
private AbstractNode right;
@Override
public String interpret() {
// TODO Auto-generated method stub
return left.interpret() + "再" +right.interpret();
}
public AndNode(AbstractNode left, AbstractNode right) {
super();
this.left = left;
this.right = right;
}
}
package interpreter;
public class SentenceNode extends AbstractNode{
private AbstractNode direction;
private AbstractNode action;
private AbstractNode distance;
public SentenceNode(AbstractNode direction, AbstractNode action, AbstractNode distance) {
super();
this.direction = direction;
this.action = action;
this.distance = distance;
}
@Override
public String interpret() {
// TODO Auto-generated method stub
return direction.interpret()+action.interpret()+distance.interpret();
}
}
package interpreter;
public class DirectionNode extends AbstractNode{
private String direction;
public DirectionNode(String direction) {
super();
this.direction = direction;
}
@Override
public String interpret() {
// TODO Auto-generated method stub
if(direction.equals("up")){
return "向上";
}else if(direction.equals("down")){
return "向下";
}else if(direction.equals("left")){
return "向左";
}else if(direction.equals("right")){
return "向右";
}else{
return "error";
}
}
}
package interpreter;
public class ActionNode extends AbstractNode{
private String action;
public ActionNode(String action) {
super();
this.action = action;
}
@Override
public String interpret() {
// TODO Auto-generated method stub
if(action.equals("move")){
return "移动";
}else if(action.equals("run")){
return "快速移动";
}else{
return "error";
}
}
}
package interpreter;
public class DistanceNode extends AbstractNode{
private String distance;
public DistanceNode(String dis) {
super();
this.distance = dis;
}
@Override
public String interpret() {
// TODO Auto-generated method stub
return this.distance;
}
}
package interpreter;
import java.util.Stack;
public class InstructionHandler {
private AbstractNode node;
public void handle(String instruction){
AbstractNode left = null;
AbstractNode right = null;
AbstractNode direction = null;
AbstractNode action = null;
AbstractNode distance = null;
Stack<AbstractNode> 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 dir = words[++i];
direction = new DirectionNode(dir);
String a = words[++i];
action = new ActionNode(a);
String dis = words[++i];
distance = new DistanceNode(dis);
right = new SentenceNode(direction, action, distance);
stack.push(new AndNode(left, right));
}else{
String dir = words[i];
direction = new DirectionNode(dir);
String a = words[++i];
action = new ActionNode(a);
String dis = words[++i];
distance = new DistanceNode(dis);
left = new SentenceNode(direction, action, distance);
stack.push(left);
}
}
this.node = (AbstractNode)stack.pop();
}
public String output(){
String result = node.interpret();
return result;
}
}
package interpreter;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
String s = "down run 10 and left move 20";
InstructionHandler handler = new InstructionHandler();
handler.handle(s);
System.out.println(handler.output());
}
}
应用场景
解释器模式为自定义语言的设计和实现提供了一种解决方案,用于定义一组文法规则并且通过文法规则来解释句子。但是解释器模式的使用频率不高,但是它在正则表达式、xml文档解释等领域还是广泛应用。
优点
- 易于改变和扩展文法。
- 每一个文法规则都可以表示一个类,因此可以方便的实现一个简单的语言。
- 实现文法较为容易
- 增加新的解释表达式较为方便
缺点
- 对于复杂的文法难以维护。一条规则至少需要定义一个类,一个语言包含太多的语法规则,类的个数急剧增加,导致系统难以维护,此时可以考虑使用语法分析程序方代替解释器模式。
- 执行效率低。解释器模式中有大量的循环和递归调用,因此在解释较为复杂的句子时速度很慢,而且代码的调试过程也较为麻烦。