解释器模式

275 阅读3分钟

这是我参与8月更文挑战的第28天,活动详情查看:8月更文挑战

解释器模式

给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。
也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。

结构

抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。具体的解释器分别由终结符解释器TerminalExpression和非终结符解释器NonterminalExpression完成

终结符表达式(Terminal Expression)角色:用来实现文法中与终结符相关的操作,通常一个解释器模式中只有一个终结符表达式,如果公式R=R1+R2,在里面R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。

非终结符表达式(Nonterminal Expression)角色:用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。公式R=R1+R2中,+就是非终结符,解析+的解释器就是一个非终结符表达式。

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

image.png

演示

1、抽象表达式

public interface Expression {
     public int interpret(HashMap<String, Integer> var);
}

2、终结者表达式

public class TerminalExpression implements Expression {
    private String key;
    public TerminalExpression(String key) {
        this.key = key;
    }
    @Override
    public int interpret(HashMap<String, Integer> var) {
        return var.get(this.key);
    }
}

3、非终结者表达式

public class NonterminalExpression implements Expression {
    protected Expression left;
    protected Expression right;
    public NonterminalExpression(Expression left, Expression right) {
         this.left = left;
         this.right = right;
    }
    @Override
    public int interpret(HashMap<String, Integer> var) {
        return 0;
    }
}

public class AddExpression extends NonterminalExpression {
    public AddExpression(Expression left, Expression right) {
        super(left, right);
    }
    public int interpret(HashMap<String, Integer> var) {
        return left.interpret(var) + right.interpret(var);
    }
}

4、环境角色

public class Calculator {
    private Expression expression;
    public Calculator(String exception) {
        // 安排运算先后顺序
        Stack<Expression> stack = new Stack<>();
        // 表达式拆分为字符数组
        char[] charArray = exception.toCharArray();
        Expression left = null;
        Expression right = null;
        for (int i = 0; i < charArray.length; i++) {
            switch (charArray[i]) {
            case '+': // 加法
                left = stack.pop();
                right = new TerminalExpression(String.valueOf(charArray[++i]));
                stack.push(new AddExpression(left, right));
                break;
            case '-': // 减法
                left = stack.pop();
                right = new TerminalExpression(String.valueOf(charArray[++i]));
                stack.push(new NonterminalExpression(left, right));
                break;
            default: // 公式中的变量
                stack.push(new TerminalExpression(String.valueOf(charArray[i])));
                break;
            }
        }
        this.expression = stack.pop();
    }

    // 计算
    public int run(HashMap<String, Integer> var) {
        return this.expression.interpret(var);
    }
}

5、客户端

public class Client {
    public static void main(String[] args) throws IOException {
        String expStr = "a+b";
        HashMap<String, Integer> map = new HashMap<String, Integer>();
        map.put("a", 314325);
        map.put("b", 143987);
        Calculator calculator = new Calculator(expStr);
        System.out.println("结果:" + expStr + "=" + calculator.run(map));
        // 结果:a+b=458312
    }
}

总结

上面的代码通过实现一个计算机加法demo展示,可以看到 1、解释器模式的扩展性是很好的,语法规则的变化只需要操作非终结者表达式就可以;同时增加一项语法就需要新增一个非终结者表达式类;如果语法复杂,会导致系统类膨胀。

2、使用了循环,解析一个复杂的语句时效率会比较慢

3、目前实现场景比较少,就算遇到也可以通过其他方式替代,如:Java中的expression4J