解释器模式

414 阅读4分钟

这是我参与2022首次更文挑战的第26天,活动详情查看:2022首次更文挑战

解释器模式

解释器模式的概念

解释器模式(Interpreter Pattern)是指给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。解释器模式在类的创建过程中属于行为型模式。

解释器模式跟字节码一样,将一些固定的语法进行解释,构建出一个解释句子的解释器。简单来讲,解释器就是一个简单语法分析工具,可以识别出句子语义、分离终结符合和非终结符号,提取出需要的信息,让程序猿针对不同的信息做出相应的处理。解释器模式的核心就是识别语法,构建解释。

解释器模式的应用场景

解释器模式在我们生活中无处不在,比如音乐的五线谱、战争时期的摩斯密码等,通常情况下解释器模式适用于以下场景

  • 当语言的文法较为简单,且执行效率不是关键问题时。

  • 当问题重复出现,且可以用一种简单的语言来进行表达时。

  • 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。

解释器模式的通用写法

一般情况下解释器模式包含以下几种角色

  • 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。

  • 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。

  • 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。

  • 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。

  • 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。、

通过一个身份解析的问题来展现解释器模式的基本写法

  • IDCardExpression(抽象表达式角色)
public abstract class IDCardExpression {
    abstract boolean interpret(String expression);
}
  • OrExpression(非终结表达式)
public class OrExpression  extends  IDCardExpression{
    private IDCardExpression cityExp;
    private IDCardExpression typeExp;

    public OrExpression(IDCardExpression cityExp, IDCardExpression typeExp) {
        this.cityExp = cityExp;
        this.typeExp = typeExp;
    }

    @Override
    boolean interpret(String expression) {

        return cityExp.interpret(expression) || typeExp.interpret(expression);
    }
}
  • TerminalExpression(终结表达式)
public class TerminalExpression  extends  IDCardExpression{

    Set<String> data;
    String symbol;

    public TerminalExpression(Set<String> data, String symbol) {
        this.data = data;
        this.symbol = symbol;
    }

    @Override
    boolean interpret(String expression) {
        String[] split = expression.split(symbol);
        boolean result =false;
        for (String s : split){
          if (data.equals(s)){
              return  true;
          }
        }
        return false;
    }
}
  • Area(上下文环境)
public class Area {
    Set<String> city = new HashSet<>();

    Set<String> type = new HashSet<>();
    IDCardExpression idCardExpression;

    public Area() {
        city.add("武汉市");
        city.add("上海市");
        type.add("医生");
        type.add("老人");
        type.add("儿童");

        TerminalExpression city = new TerminalExpression(this.city, ":");
        TerminalExpression type = new TerminalExpression(this.type, "-");
        idCardExpression = new OrExpression(city, type);
    }
    public void getTicker(String expression) {
        boolean interpret = idCardExpression.interpret(expression);

        if (interpret){
            System.out.println("免费参观");
        }else {
            System.out.println("错误");
        }

    }
}
  • UML结构图

image.png

通过上述代码和UML结构图中可以看出,通过创建抽象类IDCardExpression,包含了解释方法,然后创建终结类和非终结类同时实现抽象类card,重写interpret方法,然后在创建上下文环境Area类,用来传递解释类需要的数据,Main类就一个测试方法,无过多方法。

解释器模式的优缺点

优点

  • 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。

  • 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。

缺点

  • 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。

  • 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。

  • 可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。