解释器模式

一、解释器模式的介绍:

解释器是一种用的比较少的行为模式,其提供了一种解释语言的语法,或者表达式的方式。该模式定义了一个表达式的接口。
有两个重点需要关注的地方:
1、必须有一个抽象接口
2、构建语法树

二、应用场景

2.1简单的语言需要解释执行而且可以将该语言中的语句表示一个抽象的语法树
2.2对于某个特定的领域出现的不断重复的问题,可以转换成一种语法规则下的语句

三、解释器模式结构图

解释器模式.png

四、角色分析

** 抽象表达式角色:**声明一个抽象的解释操作,这个接口为所有具体表达式角色(抽象语法树中的节点)都要实现的。

终结符表达式角色:具体表达式。实现与文法中的终结符相关联的解释操作,而且句子中的每个终结符需要该类的一个实例与之对应。

非终结符表达式角色:具体表达式。文法中的每条规则R::=R1R2…Rn都需要一个非终结符表带式角色,对于从R1到Rn的每个符号都维护一个抽象表达式角色的实例变量,实现解释操作,解释一般要递归地调用表示从R1到Rn的那些对象的解释操作。

上下文(环境)角色:包含解释器之外的一些全局信息。

客户角色:构建(或者被给定)表示该文法定义的语言中的一个特定的句子的抽象语法树,调用解释操作。

五、例子代码实现

这里以实现3 X5 X 7 / 3的结果为例子说明
抽象表达式,定义一个方法用于表示乘或者除的表达式,由子类实现

public interface Node {
   public int interpret();
}

终结符表达式角色

public class ValueNode implements Node{

    private int value; //终结符 不能被分解
    
    public ValueNode(int value) {
        this.value = value;
    }
    
    public int interpret() {
        return value;
    }

}

非终结符表达式角色(具体的乘法或者除法交给子类去实现)

public abstract class SymbolNode implements Node{
    //非终结符
    protected Node left;
    
    protected Node right;
    
    public SymbolNode(Node left,Node right) {
        this.left = left;
        this.right = right;
    }
   
}

计算乘法

/**
 * 运算成分
 * @author dell
 *
 */
public class MulNode extends SymbolNode{

    public MulNode(Node left, Node right) {
        super(left, right);
    }

    /**
     * 计算惩罚
     */
    public int interpret() {
        System.out.println("調用乘法");
        return left.interpret()*right.interpret();
    }

}

计算除法

package com.wzc.Interoreter;

public class DivNode extends SymbolNode{

    public DivNode(Node left, Node right) {
        super(left, right);
    }

    /**
     * 计算除法
     */
    public int interpret() {
        return left.interpret()/right.interpret();
    }

}

client角色,构建抽象语法树

public class Calculate {
    
   public int build(String statement){
       
       Node left = null;
       
       Node right = null;
       
       Node lastNode = null;
       
       String[] statements = statement.split(" ");
       for(int i=0;i<statements.length;i++){
           if("*".equals(statements[i])){
               //計算乘法
               left = lastNode;
               int val = Integer.parseInt(statements[++i]);
               right = new ValueNode(val);
               //此时 新建了一个非终结符
               lastNode = new MulNode(left,right);
           }else if("/".equals(statements[i])){
               //計算除法
               left = lastNode;
               int val = Integer.parseInt(statements[++i]);
               right = new ValueNode(val);
               //此时 新建了一个非终结符
               lastNode = new DivNode(left,right);
           }else{
               //是數字 就添加進來 終結符
               lastNode = new ValueNode(Integer.parseInt(statements[i]));
           }
       }
       return lastNode.interpret();
   }
}

在main函数中打印结果

  public static void main(String[] args) {
        String contentString = "3 * 5 * 7 / 3";
        Calculate calculate = new Calculate();
        int result = calculate.build(contentString);
        System.out.println("---->"+result);
    }

六、总结

1、 每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来了非常多的麻烦。
2、解释器模式采用递归调用方法每个非终结符表达式只关心与自己有关的表达式,每个表达式需要知道最终的结果,必须一层一层地剥茧,无论是面向过程的语言还是面向对象的语言,递归都是在必要条件下使用的,它导致调试非常复杂。想想看,如果要排查一个语法错误,我们是不是要一个一个断点的调试下去,直到最小的语法单元。
3、解释器模式由于使用了大量的循环和递归,效率是个不容忽视的问题,特别是用于解析复杂、冗长的语法时,效率是难以忍受的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容