大话设计模式 笔记3 解释器模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010588262/article/details/82427976

emmm一个比较复杂的模式,一般用来做表达式的解析,估计做DSL的时候都会用到?

假如现在有个算数表达式要解析出结果:a - ( ( 4 - 5 + 6 ) - b )
里面有常量也有变量

Context
存放变量的值,a=3,b=10

Expression接口
表达式的基本元素,例如:a,4,5,-,+,具体还要其他的类继承此接口实现
包含 interpret(Context context)方法,此方法给出基本元素计算出的结果

TerminalExpression 继承 Expression
最基本的元素(所以叫Terminal),不依赖其他Expression即可算出结果,例如4,5,6常量,可直接返回自身对应的Integer值,如果是表达式a,b,从context里获取即可

NonterminalExpression 继承 Expression
可以理解为稍微复杂一些的元素例如-,+,因为它的结果需要依赖TerminalExpression才能得到,这很容易理解,运算符的两边肯定是数值,你要知道两边的数值把它们做+操作或者-操作才能得到结果嘛

那现在上代码了,结合代码更好理解:
这里写图片描述

Context

package com.hugeo.interpreter;

import java.util.HashMap;

// 为了方便起见,直接继承了HashMap
public class Context extends HashMap<String, Expression> {

  public int getValue(String key) {
    if (this.containsKey(key)) {
      return this.get(key).interpret(this);
    }
    return 0;
  }

}

Expression 接口

package com.hugeo.interpreter;

public abstract class Expression {

  // 除了interpret方法之外的东西都是给nonTerminalExpression使用的
  protected Expression left;
  protected Expression right;
  // 赋值右子表达式并返回自身
  public Expression right(Expression expression){
    this.right = expression;
    return this;
  }

  public abstract int interpret(Context context);
}

两个TerminalExpression

package com.hugeo.interpreter.terminal;

import com.hugeo.interpreter.Context;
import com.hugeo.interpreter.Expression;

public class TConstExpr extends Expression {

  private int value;

  public TConstExpr(int value){
    this.value = value;
  }

  @Override
  public int interpret(Context context) {
    // 常量直接返回本身的值
    return value;
  }
}
package com.hugeo.interpreter.terminal;

import com.hugeo.interpreter.Context;
import com.hugeo.interpreter.Expression;

public class TVariableExpr extends Expression {

  private String key;

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

  @Override
  public int interpret(Context context) {
    // 变量从Context中取值
    return context.getValue(key);
  }

}

两个NonterminalExpression

package com.hugeo.interpreter.nonterminal;

import com.hugeo.interpreter.Context;
import com.hugeo.interpreter.Expression;

public class NTMinusExpr extends Expression {

  public NTMinusExpr(Expression l){
    this.left = l;
  }

  @Override
  public int interpret(Context context) {
    return left.interpret(context) - right.interpret(context);
  }
}
package com.hugeo.interpreter.nonterminal;

import com.hugeo.interpreter.Context;
import com.hugeo.interpreter.Expression;

public class NTPlusExpr extends Expression {

  public NTPlusExpr(Expression l) {
    this.left = l;
  }

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

启动类(解析器)

package com.hugeo.interpreter;

import com.hugeo.interpreter.nonterminal.NTMinusExpr;
import com.hugeo.interpreter.nonterminal.NTPlusExpr;
import com.hugeo.interpreter.terminal.TConstExpr;
import com.hugeo.interpreter.terminal.TVariableExpr;

import java.util.Stack;

/**
 * @description: 此类不是一个单纯的main方法了,应该作为一个解析器单独封装起来
 * @author: hugeo
 * @create: 2018-09-05 15:02
 **/
public class Executor {

  public static void main(String[] args) {
    String expr = "a - ( ( 4 - 5 + 6 ) - b )";
    // 分割表达式
    String[] parts = expr.split(" ");
    // 表达式栈
    Stack<Expression> expressions = new Stack<>();
    // 存放所有左括号,用于碰到右括号时找对应关系
    Stack<Integer> leftBrackets = new Stack<>();

    for (int i = 0; i < parts.length; i++) {
      switch (parts[i]) {
        case "+":
          // 碰到运算符,把它前面的表达式弹出来,作为此+运算符的左表达式
          expressions.push(new NTPlusExpr(expressions.pop()));
          break;
        case "-":
          // 同上
          expressions.push(new NTMinusExpr(expressions.pop()));
          break;
        case "(":
          // 存左括号
          leftBrackets.push(i);
          break;
        case ")": {
          // 弹出对应的左括号的位置
          int match = leftBrackets.pop();
          // 如果这一对括号的前面没有别的括号了,或者没有紧靠在前面的左括号,并且此括号前面有别的表达式
          // 那说明此括号括住的表达式和前面的表达式应该是连着的,而且前面的表达式的右孩子应该是空着的正在等此括号运算完毕
          // 要做的就是把两个表达式弹出合并成一个
          if ((leftBrackets.empty() || match > leftBrackets.peek() + 1) && expressions.size() > 1) {
            Expression p1 = expressions.pop();
            Expression p2 = expressions.pop();
            expressions.push(p2.right(p1));
          }
          break;
        }
        default: {
          // 碰到数值要看是不是第一个字符,或者它的前面刚好是个左括号,如果满足,说明应该新起一个Terminal表达式
          // 否则应该将它作为前一个Nonterminal表达式的右孩子
          // 具体还需要判断是变量还是常量
          if (i == 0 || (!leftBrackets.empty() && leftBrackets.peek() == i - 1)) {
            if (parts[i].equals("a") || parts[i].equals("b")) {
              expressions.push(new TVariableExpr(parts[i]));
            } else {
              expressions.push(new TConstExpr(Integer.valueOf(parts[i])));
            }
          } else {
            if (parts[i].equals("a") || parts[i].equals("b")) {
              expressions.push(expressions.pop().right(new TVariableExpr(parts[i])));
            } else {
              expressions.push(expressions.pop().right(new TConstExpr(Integer.valueOf(parts[i]))));
            }
          }
        }
      }
    }

    // Context构造
    Context context = new Context();
    context.put("a", new TConstExpr(3));
    context.put("b", new TConstExpr(30));

    // 经过一系列压栈出栈之后,栈里面只会剩一个Expression,将Context传进去就可以算出结果了
    int result = expressions.pop().interpret(context);

    // 打印结果:28
    System.out.println(result);
  }

}

猜你喜欢

转载自blog.csdn.net/u010588262/article/details/82427976
今日推荐