设计模式-解释器模式


解释器模式的定义

*解释器(Interpreter)模式: * 提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等

  • 是一种不常用的设计模式 – 用于描述如何构成一个简单的语言解释器,主要用于使用面向对象语言开发的 编译器和解释器设计。
  • 当我们需要开发一种新的语言时,可以考虑使用解释器模式。
  • 尽量不要使用解释器模式,后期维护会有很大麻烦。在项目中,可以使用 Jruby,Groovy、java的js引擎来替代解释器的作用,弥补java语言的不足

特点

1.优点

  • 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
  • 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。

2. 缺点

  • 可利用场景比较少

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

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

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

模式的结构与实现

1. 模式的结构

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

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

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

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

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

    解释器模式结构图

2. 模式的实现

2.1 简单结构
//抽象表达式类
interface  AbstractExpression{
    Object interpret(String info);
}
//终结符表达式类
class TerminalExpression implements AbstractExpression{
    @Override
    public Object interpret(String info) {
        //对终结符表达式进行处理
        return null;
    }
}
//非终结符表达式类
class NonterminalExpression implements AbstractExpression{
    private AbstractExpression exp1;
    private AbstractExpression exp2;
    @Override
    public Object interpret(String info) {
        //非对终结符表达式的处理
        return null;
    }
}
//环境类
class Context{
    private AbstractExpression exp;
    public Context(){
        //数据初始化
    }
    public void operation(String info){
        //调用相关表达式类的解释方法
    }
}
2.2.实例: 算术表达式
/**
 * 使用解释器模式创建简单的算术表达式求值
 * 包含 + - * / ( )
 * jdk12+ 
 * @param <T> 终结符: 输入参数
 * @param <R> 终结符执行返回结果
 */
//抽象表达式类:(Function表示Context环境类,用来将变量解析为数字)
interface Expression<T,R>{
    R interpreter(Function<T,R> function);
}
//终结表达式类: (本例用来表示表达式中的数字字符)
class TerminalExpression implements Expression<String,Double>{
    private String variable;
    public TerminalExpression(String variable) {
        this.variable = variable;
    }
    @Override
    public Double interpreter(Function<String, Double> function) {
        return function.apply(variable);
    }
}
//抽象非终结表达式: (表示运算操作)
abstract class NonTerminalExpression<T,R> implements Expression<T,R>{
    protected Expression<T,R> e1;
    protected Expression<T,R> e2;
    public NonTerminalExpression(Expression<T, R> e1, Expression<T, R> e2) {
        this.e1 = e1;
        this.e2 = e2;
    }
}
//具体非终结表达式: (+ - * /)
class OperationAdd extends NonTerminalExpression<String,Double>{
    public OperationAdd(Expression<String, Double> e1, Expression<String, Double> e2) {
        super(e1, e2);
    }

    @Override
    public Double interpreter(Function<String, Double> function) {
        return e1.interpreter(function) + e2.interpreter(function);
    }
}
class OperationSub extends NonTerminalExpression<String,Double>{
    public OperationSub(Expression<String, Double> e1, Expression<String, Double> e2) {
        super(e1, e2);
    }
    @Override
    public Double interpreter(Function<String, Double> function) {
        return e1.interpreter(function) - e2.interpreter(function);
    }
}
class OperationMul extends NonTerminalExpression<String,Double>{
    public OperationMul(Expression<String, Double> e1, Expression<String, Double> e2) {
        super(e1, e2);
    }
    @Override
    public Double interpreter(Function<String, Double> function) {
        return e1.interpreter(function) * e2.interpreter(function);
    }
}
class OperationDiv extends NonTerminalExpression<String,Double>{
    public OperationDiv(Expression<String, Double> e1, Expression<String, Double> e2) {
        super(e1, e2);
    }

    @Override
    public Double interpreter(Function<String, Double> function) {
        return e1.interpreter(function) / e2.interpreter(function);
    }
}

public class InterpreterPattern{
    public static void main(String[] args) {
        String[] exp = {
                "( ( 1 + 2 ) * ( ( 3 - 4 ) * ( 5 - 6 ) ) )",
                "2 + 3 * 4 + 5",
                "100 * ( 2 + 12 )",
                "100 * ( 2 + 12 ) / 14",
                "(100/(3+7)+10)*(10-2)",
                "5+(3+5)/4*12",
                "5-2+3"
        };
        //测试
        Stream.of(exp).forEach( e -> {
            double result = new InterpreterPattern().preOrderExpression(e);
            System.out.println(result);
        });
    }

    //转前缀表达式
    public Double preOrderExpression(String exp){
        char[] ch = exp.replace(" ","").toCharArray();
        Deque<Character> optStack = new ArrayDeque<>();
        Deque<String> resultStack = new ArrayDeque<>();
        for(int i=ch.length-1;i>=0;--i){
            char cur = ch[i];
            if(')' == cur || "/*".contains(cur+""))
                optStack.push(cur);
            else if("+-".contains(Character.toString(cur))){
                while(!optStack.isEmpty() && "/*".contains(optStack.peek()+"")){
                    resultStack.push(optStack.pop()+"");
                }
                optStack.push(cur);
            }
            else if("0123456789.".contains(Character.toString(cur))){
                String temp = cur+"";
                while(--i >= 0 && "0123456789.".contains(ch[i]+"")){
                    temp += ch[i];
                }
                resultStack.push(new StringBuilder(temp).reverse().toString());
                ++i;
            }
            else if('(' == cur){
                while(!optStack.isEmpty() && ')' != optStack.peek()){
                    resultStack.push(optStack.pop()+"");
                }
                optStack.pop();
            }
            else{
                throw new RuntimeException("未知运算符");
            }
        }
        while(!optStack.isEmpty())
            resultStack.push(optStack.pop()+"");

        Deque<String> preOrder = new ArrayDeque<>();
        while(!resultStack.isEmpty())
            preOrder.push(resultStack.pop());

        return calcPreOrder(preOrder);
    }
    //前缀表达式计算: (使用解释器模式构建的对象)
    private Double calcPreOrder(Deque<String> stack){
        Deque<Expression<String,Double>> dataStack = new ArrayDeque<>();
        while(!stack.isEmpty()){
            String cur = stack.pop();
            dataStack.push(
                    switch(cur){
                        case "+" -> new OperationAdd(dataStack.pop(),dataStack.pop());
                        case "-" -> new OperationSub(dataStack.pop(),dataStack.pop());
                        case "*" -> new OperationMul(dataStack.pop(),dataStack.pop());
                        case "/" -> new OperationDiv(dataStack.pop(),dataStack.pop());
                        default -> new TerminalExpression(cur);
                    }
            );
        }
        return dataStack.pop().interpreter(Double::parseDouble); //参数为Context环境类
    }
}

文章作者: Bryson
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Bryson !
评论
 上一篇
下一篇 
设计模式-备忘录模式 设计模式-备忘录模式
备忘录模式的定义 *备忘录(Memento)模式: * 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态,该模式又叫快照模式 特点1.优点 提供了一种可以恢复状态的
2020-05-13
  目录