• 解释器模式


    场景引入:解释器模式用于描述如何构成一个简单的语言解释器,主要应用于使用面向对象语言开发的解释器的设计。当需要开发一个新的语言的时候可以考虑使用解释器模式

    定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表达式来解释语言中的句子

    文法规则和抽象语法树

    本文以加/减法解释器为例,每一个输入表达式(例如1+2+3-4+1)都是包含了3个语言单位,可以使用以下文法规则来定义:

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

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

    抽象语法树可以直观的表示语言的构成,每一棵抽象语法树对应一个语言实例。例如加法/减法表达式语言中的语句“1+2+3-4+1”可以通过如图所示的抽象语法树来表示:

    解释器模式结构

    解释器模式包含以下4个角色:

    (1)AbstractExpression(抽象表达式):在抽象表达式中声明了抽象解释操作,它是所有终结符和非终结符表达式的父类

    (2)TerminalExpression(终结符表达式):它实现了与文法中的终结符相关联的解释操作,在句子中的每一个终结符都是该类的一个实例

    (3)NonterminalExpression(非终结符表达式):非终结符表达式也是抽象表达式的子类,它实现了文法中非终结符的解释操作,由于在非终结符表达式中可以包含终结符表达式,也可以继续包含非终结符表达式,因此其解释操作一般通过递归的方式完成

    (4)Context(环境类):环境类又称为上下文类,它用于存储解释器之外的一些全局信息,通过它临时存储了需要解释的语句

     解释器模式应用实例

    1. 实例说明

    某公司要开发一套简单的机器人控制程序,控制程序中包含一些简单的英文控制指令,每一个指令对应一个表达式,该表达式可以是简单的表达式也可以是复合表达式,每一个简单的表达式由移动方向(上:up、下:down、左:left、右:right),移动方式(移动:move、快速移动:run),移动距离(一个正整数),两个表达式可以通过与(and)连接形成复合表达式。控制指令示例:“down run 10 and left move 5”,则“向下快速移动10个单位再向左移动5个单位”;输入“up move 10”,则“向上移动10个单位”

    2. 实例分析及类图

    根据上述的需求描述用形式化语言表达的文法规则如下:

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

    3. 代码实例

    1 package designpatterns.interpreter;
    2 
    3 public abstract class AbstractNode {
    4     public abstract String interpret();
    5 }
     1 package designpatterns.interpreter;
     2 
     3 public class AndNode extends AbstractNode {
     4     private AbstractNode left;        //And的左表达式
     5     private AbstractNode right;        //And的右表达式
     6     
     7     public AndNode(AbstractNode left, AbstractNode right) {
     8         this.left = left;
     9         this.right = right;
    10     }
    11 
    12     @Override
    13     public String interpret() {
    14         return left.interpret() + "再" + right.interpret();
    15     }    
    16 }
     1 package designpatterns.interpreter;
     2 
     3 public class SentenceNode extends AbstractNode {
     4     private AbstractNode direction;
     5     private AbstractNode action;
     6     private AbstractNode distance;
     7 
     8     public SentenceNode(AbstractNode direction, AbstractNode action, AbstractNode distance) {
     9         super();
    10         this.direction = direction;
    11         this.action = action;
    12         this.distance = distance;
    13     }
    14 
    15     @Override
    16     public String interpret() {
    17         return direction.interpret() + action.interpret() + distance.interpret();
    18     }
    19 
    20 }
     1 package designpatterns.interpreter;
     2 
     3 public class DirectionNode extends AbstractNode {
     4     private String direction;
     5 
     6     public DirectionNode(String direction) {
     7         super();
     8         this.direction = direction;
     9     }
    10 
    11     @Override
    12     public String interpret() {
    13         if(direction.equalsIgnoreCase("up")){
    14             return "向上";
    15         }
    16         else if(direction.equalsIgnoreCase("down")) {
    17             return "向下";
    18         }
    19         else if(direction.equalsIgnoreCase("left")) {
    20             return "向左";
    21         }
    22         else if(direction.equalsIgnoreCase("right")) {
    23             return "向右";
    24         }
    25         else{
    26             return "无效指令";
    27         }
    28     }
    29 }
     1 package designpatterns.interpreter;
     2 
     3 public class ActionNode extends AbstractNode {
     4     private String action;
     5     
     6     public ActionNode(String action) {
     7         super();
     8         this.action = action;
     9     }
    10     
    11     @Override
    12     public String interpret() {
    13         if(action.equalsIgnoreCase("move")) {
    14             return "移动";
    15         }
    16         else if(action.equalsIgnoreCase("run")){
    17             return "快速移动";
    18         }
    19         else{
    20             return "无效指令";
    21         }
    22     }
    23 }
     1 package designpatterns.interpreter;
     2 
     3 public class DistanceNode extends AbstractNode {
     4     private String distance;
     5     
     6     public DistanceNode(String distance) {
     7         super();
     8         this.distance = distance;
     9     }
    10 
    11     @Override
    12     public String interpret() {
    13         return this.distance;
    14     }
    15 }
     1 package designpatterns.interpreter;
     2 
     3 import java.util.Stack;
     4 
     5 public class InstructionHandler {
     6     private AbstractNode node;
     7     
     8     public void handle(String instruction){
     9         AbstractNode left = null,right = null;
    10         AbstractNode action = null,direction = null,distance = null;
    11         
    12         //声明一个栈对象用于存储抽象语法树
    13         Stack<AbstractNode> stack = new Stack<AbstractNode>();
    14         //以空格分割指令字符串
    15         String[] words = instruction.split(" ");
    16         for(int i = 0;i < words.length;i++) {
    17             /**
    18              * 本实例采用栈的方式处理指令,如果遇到and,将其后的3个单词作为3个终结
    19              * 表达式连成一个简单句子SentenceNode作为and的右表达式,而将从栈顶
    20              * 弹出的表达式作为and的左表达式,最后将新的and表达式压入栈中
    21              */
    22             if(words[i].equalsIgnoreCase("and")) {
    23                 left = (AbstractNode)stack.pop();    //弹出栈顶表达式作为左表达式
    24                 String word1 = words[++i];
    25                 direction = new DirectionNode(word1);
    26                 String word2 = words[++i];
    27                 action = new ActionNode(word2);
    28                 String word3 = words[++i];
    29                 distance = new DistanceNode(word3);
    30                 
    31                 right = new SentenceNode(direction, action, distance);
    32                 stack.push(new AndNode(left,right));    //将新表达式压入栈中
    33             }
    34             else{
    35                 String word1 = words[i];
    36                 direction = new DirectionNode(word1);
    37                 String word2 = words[++i];
    38                 action = new ActionNode(word2);
    39                 String word3 = words[++i];
    40                 distance = new DistanceNode(word3);
    41                 left = new SentenceNode(direction, action, distance);
    42                 stack.push(left);
    43             }
    44         }
    45         this.node = (AbstractNode)stack.pop();        //将全部表达式从栈中弹出
    46     }
    47     
    48     public String output(){
    49         String result = node.interpret();            //解释表达式
    50         return result;
    51     }
    52 }
     1 package designpatterns.interpreter;
     2 
     3 public class Client {
     4     public static void main(String[] args) {
     5         String instruction = "down run 10 and up move 20";
     6         InstructionHandler handler = new InstructionHandler();
     7         handler.handle(instruction);
     8         
     9         String outputString;
    10         outputString = handler.output();
    11         System.out.println(outputString);
    12     }
    13 }

    4. 结果及分析

    解释器模式优/缺与适用环境

    1.解释器模式优点

    (1)易于改变和扩展文法

    (2)每一条文法规则都可以表示为一个类,因此可以方便的实现一个简单的语言

    (3)实现文法较为容易

    (4)增加新的解释表达式较为方便

    2.解释器模式缺点

    (1)对于复杂的文法难以维护

    (2)执行效率低

    3. 解释器模式的适用环境

    (1)可以将一个需要解释执行的语言中的句子表达为一个抽象语法书

    (2)一些重复出现的问题可以用一个简单的语言进行表达

    (3)一个语言的文法比较简单

    (4)执行效率不是关键问题

  • 相关阅读:
    tool公用工具方法
    angular5.x 拦截器 switchMap
    angular5.x拦截器 给get post请求添加参数user_token
    flex布局 阮一峰
    json.stringify()和json.parse()
    年份月数天数
    打印字母塔
    打印形状
    打印九九乘法表
    C语言猜数字游戏
  • 原文地址:https://www.cnblogs.com/remote/p/10718240.html
Copyright © 2020-2023  润新知