第18章 解释器模式

18.1 解释器模式概述

image

解释器模式(Interpreter Pattern):定义一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

  • 类行为型模式

18.2 文法规则和抽象语法树

解释器模式描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。
在正式分析解释器模式结构之前先来学习如何表示一个语言的文法规则以及如何构造一棵抽象语法树

\(1+2+3-4+1\) 包含了三个语言单位:

expression :: = value | operation
operation :: = expression '+' expression | expression '-' expression
value :: = an integer//一个整数值

该文法规则包含3条语句,第一条表示表达式的组成方式,其中 value 和 operation 是后面两个语言单位的定义,每一条语句所定义的字符串(如 operation 和 value)称为语言构造成分或语言单位,符号“:: =”是“定义为”的意思,其左边的语言单位通过右边来进行说明和定义,语言单位对应终结符表达式和非终结符表达式。例如本规则中的operation是非终结符表达式,它的组成元素仍然可以是表达式,可以进一步分解,而 value 是终结符表达式,它的组成元素是最基本的语言单位,不能再进行分解。

除了使用文法规则来定义一个语言外,在解释器模式中还可以通过一种称为抽象语法树(Abstract Syntax Tree,AST) 的图形方式来直观地表示语言的构成,每一棵抽象语法树对应一个语言实例,例如加法/减法表达式语言中的语句“1+2+3-4+1”可以通过如图所示的抽象语法树来表示:
image

在该抽象语法树中可以通过终结符表达式 value 和非终结符表达式 operation 组成复杂的语,每个文法规则的语言实例都可以表示为一个抽象语法树,即每一条具体的语句都可以用类似图所示的抽象语法树来表示,在图中终结符表达式类的实例作为树的叶子结点,而非终结符表达式类的实例作为非叶子结点,它们可以将终结符表达式类的实例以及包含终结符和非终结符实例的子表达式作为其子结点。抽象语法树描述了如何构成一个复杂的句子,通过对抽象语法树的分析可以识别出语言中的终结符类和非终结符类。

18.3 解释器模式结构与实现

18.3.1 解释器模式结构

image

  1. AbstractExpression(抽象表达式):在抽象表达式中声明了抽象的解释操作,它是所有终结符表达式和非终结符表达式的公共父类。
  2. TerminalExpression(终结符表达式):终结符表达式是抽象表达式的子类,它实现了与文法中的终结符相关联的解释操作,在句子中的每一个终结符都是该类的一个实例。通常在一个解释器模式中只有少数几个终结符表达式类,它们的实例可以通过非终结符表达式组成较为复杂的句子。
  3. NonterminalExpression(非终结符表达式):非终结符表达式也是抽象表达式的子类,它实现了文法中非终结符的解释操作,由于在非终结符表达式中可以包含终结符表达式,也可以继续包含非终结符表达式,因此其解释操作一般通过递归的方式完成。
  4. Context(环境类):环境类又称为上下文类,它用于存储解释器之外的一些全局信息,通常它临时存储了需要解释的语句。

18.3.2 解释器模式实现

18.4 解释器模式应用实例

实例说明

       某软件公司要开发一套机器人控制程序,在该机器人控制程序中包含一些简单的英文控制指令,每一个指令对应一个表达式(expression),该表达式可以是简单表达式也可以是复合表达式,每一个简单表达式由移动方向(direction),移动方式(action)和移动距离(distance)3部分组成,其中移动方向包括上(up)、下 (down)、左(left)、右(right);移动方式包括移动(move)和快速移动(run);移动距离为一个正整数。两个表达式之间可以通过与(and)连接形成复合(composite)表达式。
       用户通过对图形化的设置界面进行操作可以创建一个机器人控制指令,机器人在收到指令后将按照指令的设置进行移动,例如输入控制指令“up move 5”,则“向上移动5个单位”;输入控制指令“down run 10 and left move 20”,则“向下快速移动10个单位再向左移动20个单位”。
现使用解释器模式来设计该程序并模拟实现。

实例分析及类图

expression ::= direction action distance | composite//表达式
composite ::= expression 'and' expression//复合表达式
direction :: = 'up' | 'down' | 'left' | 'right'//移动方向
action ::= 'move' | 'run'//移动方式
distance ::= an integer//移动距离

五条文法规则,对应五个语言单位:
分为两类:

  1. 终结符
  2. 非终结符
  • 终结符表达式
    • direction
      • DirectionNode
    • action
      • ActionNode
    • distance
      • DistanceNode
  • 非终结表达式
    • expression
      • SentenceNode
    • composite
      • AndNode

举例:“down run 10 and left move 20”
image

  • 抽象表达式角色
    • AbstractNode
  • 终结符表达式角色
    • DirectionNode
    • ActionNode
    • DistanceNode
  • 非终结符表达式角色
    • AndNode
    • SentenceNode

实例代码

AbstractNode:抽象结点类,充当抽象表达式角色。
package designpatterns.interpreter;

public abstract class AbstractNode {
    public abstract String interpret();
}
AndNode: And 结点类,充当非终结符表达式角色。
package designpatterns.interpreter;

public class AndNode extends AbstractNode {
    private AbstractNode left; //And 的左表达式
    private AbstractNode right; //And的右表达式

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

    //And表达式解释操作
    @Override
    public String interpret() {
        return left.interpret() + "再" + right.interpret();
    }
}
SentenceNode:简单句子结点类,充当非终结符表达式角色。
package designpatterns.interpreter;

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;
    }

    //简单句子的解释操作
    @Override
    public String interpret() {
        return direction.interpret() + action.interpret() + distance.interpret();
    }
}
DirectionNode:方向结点类,充当终结符表达式角色。
package designpatterns.interpreter;

public 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 "无效指令";
        }
    }
}
ActionNode:动作结点类,充当终结符表达式角色。
package designpatterns.interpreter;

public 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 "无效指令";
        }
    }
}
DistanceNode:距离结点类,充当终结符表达式角色。
package designpatterns.interpreter;

public class DistanceNode extends AbstractNode {
    private String distance;

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

    //距离表达式的解释操作
    @Override
    public String interpret() {
        return this.distance;
    }
}
InstructionHandler:指令处理类,工具类,提供相应的方法对输入指令进行处理。它将输入指令分割为字符串数组,将第一个、第二个和第三个单词组合成一个句子,并存入栈中;如果发现有单词"“and”,则将“and”后的第一个、第二个和第三个单词组合成一个新的句子作为“and”的右表达式,并从栈中取出原先所存的句子作为左表达式,然后组合成一个 And 结点存入栈中。依此类推,直到整个指令解析结束。
package designpatterns.interpreter;

import java.util.Stack;

public class InstructionHandler {
    private AbstractNode node;

    public void handle(String instruction) {
        AbstractNode left = null, right = null;
        AbstractNode direction = null, action = null, distance = null;
        Stack<AbstractNode> stack = new Stack<AbstractNode>();//声明一个栈对象用于存储抽象语法树
        String[] words = instruction.split(" ");//以空格分隔指令字符串
        for (int i = 0; i < words.length; i++) {
            //本实例采用栈的方式处理指令,如果遇到"and",则将其后的3个单词作为3个终
            //结符表达式连成一个简单句子SentenceNode作为"and"的右表达式,而将从栈顶
            //弹出的表达式作为"and"的左表达式,最后将新的"and"表达式压入栈中.
            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));//将新表达式压入栈中
            }
            //如果是从头开始进行解释,则将前3个单词组成一个简单句子SentenceNode并将
            //该句子压入栈中
            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:客户端测试类。
package designpatterns.interpreter;

public class Client {
    public static void main(String[] args) {
        String instruction = "down run 10 and left move 20";
        InstructionHandler handler = new InstructionHandler();
        handler.handle(instruction);
        String outString;
        outString = handler.output();
        System.out.println(outString);
    }
}

结果及分析

image

posted @ 2022-04-20 19:39  手持六脉神剑的外星人  阅读(39)  评论(0编辑  收藏  举报