珍惜当下 展望未来!

解释器模式

解释器模式

1.介绍

image-20211215151021238

如上图,设计一个软件用来进行加减计算。我们第一想法就是使用工具类,提供对应的加法和减法的工 具方法

image-20211215151045859

上面的形式比较单一、有限,如果形式变化非常多,这就不符合要求,因为加法和减法运算,两个运算 符与数值可以有无限种组合方式。

比如 1+2+3+4+5、1+2+3-4等等。

显然,现在需要一种翻译识别机器,能够解析由数字以及 + - 符号构成的合法的运算序列。

如果把运 算符和数字都看作节点的话,能够逐个节点的进行读取解析运算,这就是解释器模式的思维。

定义:

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

在解释器模式中,我们需要将待解决的问题,提取出规则,抽象为一种“语言”。

比如加减法运算,规则 为:由数值和+-符号组成的合法序列,“1+3-2” 就是这种语言的句子。

解释器就是要解析出来语句的含义。但是如何描述规则呢?

image-20211215151513207

结构:

解释器模式包含以下主要角色。

  • 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主 要包含解释方法 interpret()。
  • 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结 符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
  • 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法 中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
  • 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有 解释器共享的数据,后面的解释器可以从这里获取这些值。
  • 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语 法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法

2.实现

实现加减法的软件

抽象表达式

//抽象表达式
public abstract class AbstractExpression {
    //解释的方法
    public abstract int interpret(Context context);
}

非终结符表达式 变量

//非终结符表达式 变量
public class Variable extends AbstractExpression{

    private String name;//变量名字

    public Variable(String name) {
        this.name = name;
    }

    //获取变量的值
    @Override
    public int interpret(Context context) {
        return context.getValue(this);
    }
}

终结符表达式:加法和减法

//终结符表达式 加法
public class Add extends AbstractExpression {

    //定义两个变量
    private AbstractExpression left;
    private AbstractExpression right;

    public Add(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret(Context context) {
        //左边变量+右边变量
        return left.interpret(context) + right.interpret(context);
    }
}



//终结符表达式  减法
public class Minus extends AbstractExpression {
    private AbstractExpression left;
    private AbstractExpression right;

    public Minus(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret(Context context) {
        return left.interpret(context) - right.interpret(context);
    }
}

环境角色

//环境类
public class Context {
    //共享的变量
    Map<Variable,Integer> map = new HashMap<>();

    //添加变和对应的值
    public void add(Variable variable,Integer value){
        map.put(variable,value);
    }

    //获取变量对应的值
    public Integer getValue(Variable variable){
        return map.get(variable);
    }
}

测试

public class Test {
    public static void main(String[] args) {
        //定义三个变量
        Variable a= new Variable("a");
        Variable b= new Variable("b");
        Variable c= new Variable("c");

        //放到context中,并设置值
        Context context = new Context();
        context.add(a,1);
        context.add(b,9);
        context.add(c,3);

        //计算a+b-c
        AbstractExpression expression = new Minus(new Add(a,b),c);
        //执行表达式的结果
        int interpret = expression.interpret(context);
        System.out.println(interpret);
    }
}

3.优缺点

1,优点:

  • 易于改变和扩展文法。

    由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。

  • 实现文法较为容易。

    在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂。

  • 增加新的解释表达式较为方便。

    如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合 "开闭原则"。

2,缺点:

  • 对于复杂文法难以维护。

    在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护。

  • 执行效率较低。

    由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。

4.使用场景

  • 当语言的文法较为简单,且执行效率不是关键问题时。
  • 当问题重复出现,且可以用一种简单的语言来进行表达时。
  • 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候。
posted @ 2021-12-15 16:44  嘿嘿-  阅读(117)  评论(0)    收藏  举报