设计模式——解释器模式

今天我们来学习解释器模式,比起命令模式,解释器模式更加小众,只会在一些特定的领域会被用到,比如编译器、规则引擎、正则表达式。所以,解释器模式也不是学习的重点,稍微了解一下。

概述

解释器模式:(Interpreter Design Pattern)解释器模式为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法。

今天要学习的解释器模式,其实就是用来实现根据语法规则解读“句子”的解释器。

何时使用:

  • 如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

UML 类图:

image.png
角色组成:

  1. AbstractExpression:定义解释器的接口,约定解释器的解释操作

  2. TerminalExpression:终结符解释器,用来实现语法规则中和终结符相关的操作,不再包含其它的解释器,如果用组合模式来构建抽象语法树的话,就相当于组合模式中的叶子对象,可以有多种终结符解释器

  3. NonterminalExpression:非终结符解释器,用来实现语法规则中非终结符相关的操作,通常一个解释器对应一个语法规则,可以包含其它的解释器,如果用组合模式来构建抽象语法树的话,就相当于组合模式中的组合对象,可以有多种非终结符解释器

  4. Context:上下文,通常包含各个解释器需要的数据,或是公共的功能

  5. Client:客户端,指的是使用解释器的客户端,通常在这里去把按照语言的语法做的表达式,转换成为使用解释器对象描述的抽象语法树,然后调用解释操作。

通用代码

Context环境角色

public class Context {
    
    

    private String input;
    private String output;

    public String getInput() {
    
    
        return input;
    }
    public void setInput(String input) {
    
    
        this.input = input;
    }
    public String getOutput() {
    
    
        return output;
    }
    public void setOutput(String output) {
    
    
        this.output = output;
    }

}

抽象表达式

/**
 * 抽象表达式
 */
public abstract class AbstractExpression {
    
    
    /**
     * 解释的操作
     * @param ctx 上下文对象
     */
    public abstract void interpret(Context ctx);
}

终结符表达式

/**
 * 终结符表达式
 */
public class TerminalExpression extends AbstractExpression{
    
    
    public void interpret(Context ctx) {
    
    
       //实现与语法规则中的终结符相关联的解释操作
       System.out.println("终结符表达式");
    }
}

非终结符表达式

/**
 * 非终结符表达式
 */
public class NonterminalExpression extends AbstractExpression{
    
    
    public void interpret(Context ctx) {
    
    
       //实现与语法规则中的非终结符相关联的解释操作
       System.out.println("非终结符表达式");
    }
}

Client客户端

public class Client {
    
    

    public static void main(String[] args) {
    
    
        Context context = new Context();
        List<AbstractExpression> list = new ArrayList<>();

        list.add(new TerminalExpression());
        list.add(new NonterminalExpression());
        list.add(new TerminalExpression());

        for (AbstractExpression abstractExpression : list) {
    
    
            abstractExpression.Interpret(context);
        }
    }

}

其中list为一个语法容器,容纳一个具体的表达式。通常Client是一个封装类,封装的结果就是传递进来一个规范语法文件,解析器分析后产生结果并返回,避免了调用者与语法分析器的耦合关系。

结果:

终结符表达式

非终结符表达式

终结符表达式

总结

为什么会有这个解释器模式呢,从编译原理中可以受到一些启发,使用了这样的一个解释器可以在Java语言之上再定义一层语言,这种语言通过Java编写的解释器可以放到Java环境中去执行,这样如果用户的需求发生变化,比如打算做其他事情的时候,只用在自己定义的新的语言上进行修改,对于Java编写的代码不需要进行任何的修改就能在Java环境中运行,这是非常有用的。这就好像,虽然不管怎么编译,最终由中间代码生成最终代码(机器码)是依赖于相应的机器的。但是编译器却能理解高级语言和低级语言,无论高级语言的程序是怎么样编写的编译器的代码是不用修改的而解释器模式就是想做一个建立在Java和我们自定义语言之间的编译器

解释器模式的代码实现比较灵活,没有固定的模板。我们前面说过,应用设计模式主要是应对代码的复杂性,解释器模式也不例外。它的代码实现的核心思想,就是将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。一般的做法是,将语法规则拆分一些小的独立的单元,然后对每个单元进行解析,最终合并为对整个语法规则的解析。

猜你喜欢

转载自blog.csdn.net/zhang33565417/article/details/121442864
今日推荐