解释器模式

解释器模式

一、解释器模式

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

定义给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。
使用频率:1星
模式结构
1、抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
2、终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
3、非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
4、环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
5、客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
解释器模式结构图
解释器模式
优点
1、扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
2、容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
缺点
1、执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
2、会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
3、可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
应用场景
1、当语言的文法较为简单,且执行效率不是关键问题时。
2、当问题重复出现,且可以用一种简单的语言来进行表达时。
3、当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。

注意:解释器模式在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题。如果碰到对表达式的解释,在 Java 中可以用 Expression4J 或 Jep 等来设计。

二、解释器模式实例之机器人控制程序

1.实例说明

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

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个单位”。现使用解释器模式来设计该程序并模拟实现。

2.实例类图

解释器模式

3.实例代码

本例中AbstractNode充当抽象表达式角色,DirectionNode、ActionNode和DistanceNode充当终结符表达式角色,AndNode和SentenceNode充当非终结符表达式角色。

import java.util.*;

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

//And解释:非终结符表达式
class AndNode extends AbstractNode{
    private AbstractNode left;
    private AbstractNode right;

    public AndNode(AbstractNode left, AbstractNode right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public String interpret() {
        return left.interpret()+"再"+right.interpret();
    }
}

//简单句子解释:非终结符表达式
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;
    }

    @Override
    public String interpret() {
        return direction.interpret()+action.interpret()+distance.interpret();
    }
}

//方向解释:终结符表达式
class DirectionNode extends AbstractNode{
    private String direction;

    public DirectionNode(String direction) {
        this.direction = direction;
    }

    @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 "无效指令";
        }
    }
}

//动作解释:终结符表达式
class ActionNode extends AbstractNode{
    private String action;

    public ActionNode(String action) {
        this.action = action;
    }

    @Override
    public String interpret() {
        if(action.equalsIgnoreCase("move")){
            return "移动";
        }else if(action.equalsIgnoreCase("run")){
            return "快速移动";
        }else{
            return "无效指令";
        }
    }
}

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

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

    @Override
    public String interpret() {
        return this.distance;
    }
}
//指令处理类:工具类
class InstructionHandler{
    private String instruction;
    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(){
        return node.interpret();
    }
}

客户端测试:

public 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=handler.output();
        System.out.println(outString);
    }
}

运行结果:

向上移动5再向下快速移动10再向左移动5

为了便于理解上述代码流程,给出如下抽象语法树:
解释器模式
解释器模式在生成中应用甚少,故读者只做了解即可,如果对语法树感兴趣可以深入了解

参考文献

【1】解释器模式(详解版)
【2】设计模式实训教程(第2版) 刘伟 编著 清华大学出版社

上一篇:数据结构与算法—>栈


下一篇:初步认识Typescript(9)——枚举