行为型设计模式 – 解释器模式详解

基本介绍

解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。

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

该模式对于复杂的场景实现起来比较困难,实际应用较少,大家了解即可。

模式结构

Context(环境角色):声明一个所有具体表达式都要实现的抽象接口(或者抽象类),接口中主要是一个interpret() 方法,称为解释操作。具体解释任务由它的各个实现类来完成,具体的解释器分别由终结符解释器 TerminalExpression 和非终结符解释器 NonterminalExpression 完成。

AbstractExpression(抽象解释器):实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。终结符一半是文法中的运算单元,比如有一个简单的公式R=R1+R2,在里面 R1 和 R2 就是终结符,对应的解析 R1 和 R2 的解释器就是终结符表达式。

TerminalExpression(终结符表达式):文法中的每条规则对应于一个非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式 R=R1+R2 中,+ 就是非终结符,解析 + 的解释器就是一个非终结符表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。

NoterminalExpression(非终结符表达式):这个角色的任务一般是用来存放文法中各个终结符所对应的具体值,比如 R=R1+R2,我们给 R1 赋值 100,给 R2 赋值 200。这些信息需要存放到环境角色中,很多情况下我们使用 Map 来充当环境角色就足够了。

举例说明

使用解释器模式实现数字的加减法

1、抽象解释器

/**
 * 抽象解释器
 */
public abstract class AbstractExpression {
    public abstract int interpret(Context context);
}

2、非终结符表达式

/**
 * 非终结表达式:加法
 */
public class AddExpression extends AbstractExpression {
    private final AbstractExpression left;
    private final AbstractExpression right;

    public AddExpression(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 SubExpression extends AbstractExpression {
    private final AbstractExpression left;
    private final AbstractExpression right;

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

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

3、终结表达式

/**
 * 终结表达式:变量
 */
public class Variable extends AbstractExpression {
    private final String key;

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

    @Override
    public int interpret(Context context) {
        return context.getValue(key);
    }
}

4、环境角色

/**
 * 环境上下文
 */
public class Context {
    private final Map<String, Integer> valueMap = new HashMap<>();

    public void addValue(final String key, final int value) {
        valueMap.put(key, value);
    }

    public int getValue(final String key) {
        return valueMap.get(key);
    }

    public Map<String, Integer> getValueMap() {
        return valueMap;
    }
}

5、测试类

public class Client {
    @Test
    public void test(){
        Context context = new Context();

        context.addValue("a", 6);
        context.addValue("b", 9);
        context.addValue("c", 1);

        Variable a = new Variable("a");
        Variable b = new Variable("b");
        Variable c = new Variable("c");

        AbstractExpression addValue = new AddExpression(a, b);
        AbstractExpression subValue = new SubExpression(addValue, c);

        System.out.println(context.getValueMap());
        System.out.println("a + b - c = " + subValue.interpret(context));
    }
}

6、运行结果

{a=6, b=9, c=1}
a+b-c=14

模式分析

优点:

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

缺点:

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

适用场景:

  • 有一个简单的语法规则,比如一个 sql 语句,如果我们需要根据 sql 语句进行 rm 转换,就可以使用解释器模式来对语句进行解释。
  • 一些重复发生的问题,比如加减乘除四则运算,但是公式每次都不同,有时是 a+b-cd,有时是 ab+c-d,等等等等个,公式千变万化,但是都是由加减乘除四个非终结符来连接的,这时我们就可以使用解释器模式。

参考