【设计模式】第二十章:解释器模式详解及应用案例

解释器模式是一种创建型设计模式,它提供了一种如何将简单的语言解析为对象结构的方法。文章通过数学表达式解析的例子展示了如何定义抽象表达式接口、终端表达式、非终端表达式以及上下文,解释了模式的工作原理和优缺点。

系列文章

【设计模式】七大设计原则
【设计模式】第一章:单例模式
【设计模式】第二章:工厂模式
【设计模式】第三章:建造者模式
【设计模式】第四章:原型模式
【设计模式】第五章:适配器模式
【设计模式】第六章:装饰器模式
【设计模式】第七章:代理模式
【设计模式】第八章:桥接模式
【设计模式】第九章:外观模式 / 门面模式
【设计模式】第十章:组合模式
【设计模式】第十一章:享元模式
【设计模式】第十二章:观察者模式
【设计模式】第十三章:模板方法模式
【设计模式】第十四章:策略模式
【设计模式】第十五章:责任链模式
【设计模式】第十六章:迭代器模式
【设计模式】第十七章:状态模式
【设计模式】第十八章:备忘录模式
【设计模式】第十九章:访问者模式



解释器模式

一、定义

**摘自百度百科:**定义了一个解释器,来解释给定语言和文法的句子。其实质是把语言中的每个符号定义成一个(对象)类,从而把每个程序转换成一个具体的对象树。


二、角色分类

抽象表达式/抽象解释器(Abstract Expression)

声明了一个所有具体表达式都要实现的抽象接口或抽象类,接口中存在一个解释操作的方法

终止符表达式(Terminal Expression)

实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符

非终止符表达式(Nonterminal Expression)

文法中的每条规则对应于一个非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字

上下文(Context)

该角色的任务一般是用来存放各个表达式所对应的值和定义的法则

客户角色(Client)

具体调用方法的角色


三、实现方式

UML图

在这里插入图片描述

具体实现

接下来我们用解析数学表达式的示例来深入说明一下解释器模式

抽象解释器(Abstract Expression)

public interface AbstractExpression {
  /**
   * 解释操作
   */
  Object interpret();
}

终止符表达式(Terminal Expression)

public abstract class TerminalExpression implements AbstractExpression {
  protected AbstractExpression expressionA;
  protected AbstractExpression expressionB;

  public TerminalExpression(AbstractExpression expressionA, AbstractExpression expressionB) {
    this.expressionA = expressionA;
    this.expressionB = expressionB;
  }
}

非终止符表达式(Nonterminal Expression)

/**
 * 相加
 */
public class AddNonterminalExpression extends TerminalExpression {
  public AddNonterminalExpression(AbstractExpression expressionA, AbstractExpression expressionB) {
    super(expressionA, expressionB);
  }

  public int interpret() {
    return this.expressionA.interpret() + this.expressionB.interpret();
  }
}

/**
 * 相减
 */
public class SubNonterminalExpression extends TerminalExpression {
  public SubNonterminalExpression(AbstractExpression expressionBAbstractExpression expressionA, AbstractExpression expressionB) {
    return this.expressionA.interpret() - this.b.interpret();
  }
}

public class NumNonterminalExpression implements AbstractExpression {
  private int value;

  public NumNonterminalExpression(int value) {
    this.value = value;
  }
  
  public int interpret() {
    return this.value;
  }
}

上下文对象(Context)

public class Context {
  private Stack<AbstractExpression> stack = new Stack<AbstractExpression>();

  public Context(String expression) {
    this.parse(expression);
  }
  
  /**
   * 解析表达式
   */
  private void parse(String expression) {
    String[] elements = expression.split(" ");
    AbstractExpression expressionA, expressionB;

    for (int i = 0; i <elements..length; i++) {
      String operator = elements[i];
      if (Context.isOperator(operator)) {
        aExpr = this.stack.pop();
        System.out.println("出栈: " + aExpr.interpret());
        bExpr = new NumNonterminalExpression(Integer.valueOf(elements[++i]));
        TerminalExpression res = CalculatorContext.util(aExpr, bExpr, operator);
        this.stack.push(res);
        System.out.println("计算: " + aExpr.interpret() + operator + bExpr.interpret());
        System.out.println("计算结果: " + res.interpret() + " 入栈");
      } else {
        NumNonterminalExpression numNonterminalExpression = new NumNonterminalExpression(Integer.valueOf(elements[i]));
        this.stack.push(numNonterminalExpression);
        System.out.println("入栈: " + numNonterminalExpression.interpret());
      }
    }
  }

  /**
   * 计算结果
   */
  public int caculate() {
    int interpret = this.stack.pop().interpret();
    System.out.println("计算结果为:" + interpret + " 出栈");
    return interpret;
  }

  /**
   * 计算结果
   */
  public static TerminalExpression util(IExpression a, IExpression b, String symbol) {
    if (symbol.equals("+")) {
      return new AddNonterminalExpression(a, b);
     } else if (symbol.equals("-")) {
      return new SubNonterminalExpression(a, b);
     } else {
      return null;
    }
  }

  public static boolean isOperator(String symbol) {
    return (symbol.equals("+") || symbol.equals("-"));
  }
}

客户角色(Client)

public class Client {
  public static void main(String[] args) {
    // 创建上下文对象进行解释
    CalculatorContext calculatorContext = new CalculatorContext("1 + 4");
    // 获取执行结果
    System.out.println("calculatorContext.calculate() = " + calculatorContext.calculate());

    CalculatorContext calculatorContext2 = new CalculatorContext("1 + 4 - 5");
    System.out.println("calculatorContext.calculate() = " + calculatorContext2.calculate());
  }
}

运行结果

入栈: 1
出栈: 1
计算: 1+4
计算结果: 5 入栈
计算结果: 5 出栈
calculatorContext.calculate() = 5
入栈: 1
出栈: 1
计算: 1+4
计算结果: 5 入栈
出栈: 5
计算: 5-5
计算结果: 0 入栈
计算结果: 0 出栈
calculatorContext.calculate() = 0

四、应用场景

以下部分内容摘自菜鸟教程

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

主要解决: 对于一些固定文法构建一个解释句子的解释器。

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

如何解决: 构建语法树,定义终结符与非终结符。

关键代码: 构建环境类,包含解释器之外的一些全局信息,一般是 HashMap。

应用实例: 编译器、运算表达式计算。

使用场景:

  1. 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
  2. 一些重复出现的问题可以用一种简单的语言来进行表达。
  3. 一个简单语法需要解释的场景。

**注意事项:**可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。


五、优缺点

优点

  1. 可扩展性比较好,灵活。
  2. 增加了新的解释表达式的方式。
  3. 易于实现简单文法。

缺点

  1. 可利用场景比较少。
  2. 对于复杂的文法比较难维护。
  3. 解释器模式会引起类膨胀。
  4. 解释器模式采用递归调用方法。

推荐

关注博客和公众号获取最新文章

Bummon’s BlogBummon’s Home公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bummon.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值