用 JAVA 捋一下设计模式 23- 解释器模式
- 2022 年 4 月 03 日
本文字数:7510 字
阅读完需:约 25 分钟
概念及定义
解释器模式在设计模式中利用率是比较低的,且也是比较难以理解的。你可以创造一种新的具有固定语法规则和语义的语言,你可以利用解释器模式来还原这门语言。例如可以将3*2+(2*(3+4))*3/2+(2*2)-7这行字符串还原成 Java 可以理解的数字运算。
解释器模式中有几个概念:
非终结符:语言中可拆分的元素。
2*(3+4)即为非终结符。终结符:语言中不可拆分的最小元素。
2即为终结符。文法:类似于语法,每种语言都有自己的规则。例子中
+-*/不能连续出现。语句:由终结符构成的符合文法的基本单位。
3*2即为语句。语法树: 语句的组成结构的树形表示。
解释器模式的主要思想:
工厂类的生产接口以枚举类为参数,在内部根据枚举类生产出对应的产品并返回给调用者。
解释器模式中角色以及职责:
AbstractExpression(抽象表达式)
TerminalExpression,NonterminalExpression 的抽象父类,定义了用于语义解释以及语法解释的公共解释方法。
TerminalExpression(终结符表达式)
用于解释目标语言中的终结符。
NonterminalExpression(非终结符表达式)
用于解释目标语言中的非终结符。
Context(环境类)
环境类定义了一些上下文信息。
Client(客户端)
用来封装解释器的操作。提供统一的对外方法。
解释器模式的优缺点:
优点:
在处理有一定规律且重复性的数据的时候,可以利用解释器模式进行解析。
对文法的修改以及扩展比较方便。
缺点:
一般在解释器中会使用到大量迭代以及递归,执行效率低。
难以理解,利用率较低。
我们在这就利用解释器模式对3*2+(2*(3+4))*3/2+(2*2)-7这行字符串进行解释,且利用 Java 计算出结果。
实现步骤:
定义一个双向迭代器接口,用于向前或者向后对字符串进行遍历。
/** * 双向迭代器接口 */public interface Iterator<E> { //获取第一个元素 public E getFirst();
//获取最后一个元素 public E getEnd();
//是否有下个元素 public boolean hasNext();
//是否有前一个元素 public boolean hasPrevious();
//返回当前指向元素 public E getCurrent();
//返回当前指向元素index public Integer getCurrentIndex();
//迭代器指向下一个元素,并返回该元素 public E getNext();
//迭代器指向上一个元素,并返回该元素 public E getPrevious();}import java.util.List;
public class GeneralIterator<E> implements Iterator<E>{
private List<E> list;
private Integer curIndex;
public GeneralIterator(List<E> list) { this.list = list; curIndex = -1; }
public List<E> getList() { return list; }
public void setList(List<E> list) { this.list = list; }
public Integer getCurIndex() { return curIndex; }
public void setCurIndex(Integer curIndex) { this.curIndex = curIndex; }
@Override public E getFirst() { curIndex = 0; return list.get(curIndex); }
@Override public E getEnd() { curIndex = list.size()-1; return list.get(curIndex); }
@Override public boolean hasNext() { return curIndex < list.size() - 1; }
@Override public boolean hasPrevious() { return curIndex > 0; }
@Override public E getCurrent() { return list.get(curIndex); }
@Override public Integer getCurrentIndex() { return curIndex; }
@Override public E getNext() { E obj = list.get(++curIndex); if(obj==null){ System.out.println("越界"); } return obj; }
@Override public E getPrevious() { E obj = list.get(--curIndex); if(obj==null){ System.out.println("越界"); } return obj; }}2.定义一个计算器类(环境类):
import java.util.ArrayList;import java.util.List;
public class Computer { //原始表达式字符串 private final String originStr;
//原始表达式字符串对应的Character数组 private final List<Character> originChars;
//进行一次数学运算的左操作数 private String leftOperateNumber;
//进行一次数学运算的右操作数 private String rightOperateNumber;
//进行一次数学运算的操作符 private String operator;
public Computer(String originStr) { this.originStr = originStr.replace(" ",""); char[] chars = originStr.toCharArray(); originChars= new ArrayList<>(); for(char c:chars){ originChars.add(c); } }
public GeneralIterator<Character> getIterator() { return new GeneralIterator<>(originChars); }
public String getOriginStr() { return originStr; }
public List<Character> getOriginChars() { return originChars; }
public String getLeftOperateNumber() { return leftOperateNumber; }
public void setLeftOperateNumber(String leftOperateNumber) { this.leftOperateNumber = leftOperateNumber; }
public String getRightOperateNumber() { return rightOperateNumber; }
public void setRightOperateNumber(String rightOperateNumber) { this.rightOperateNumber = rightOperateNumber; }
public String getOperator() { return operator; }
public void setOperator(String operator) { this.operator = operator; }}3.抽象表达式接口
public interface IExpression { public String interpreter(Computer computer);}4.自定义异常类
public class GrammaticalErrorException extends RuntimeException{ public GrammaticalErrorException(String msg) { super(msg); }}5.定义语法 Expression 对语法进行检测
public class GrammarExpression implements IExpression{
private Boolean frontIsOperator = true;
private Integer bracketCheck = 0;
private Integer operatorCount =0;
/** * 表达式语法检测 * @param computer * @return */ @Override public String interpreter(Computer computer) { GeneralIterator<Character> iterator = computer.getIterator(); while(iterator.hasNext()){ Character curChar = iterator.getNext(); switch (curChar){ case '+': case '-': case '*': case '/': if(frontIsOperator){ throw new GrammaticalErrorException("表达式操作符使用错误。"); } frontIsOperator = true; operatorCount++; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': frontIsOperator = false; break; case '(': bracketCheck ++; frontIsOperator = true; break; case ')': bracketCheck --; if(bracketCheck<0){ throw new GrammaticalErrorException("表达式括号语法校验错误。"); } if(frontIsOperator){ throw new GrammaticalErrorException("表达式操作符使用错误。"); } break; default: throw new GrammaticalErrorException("表达式含有非法字符。"); }
} if(bracketCheck!=0){ throw new GrammaticalErrorException("表达式括号数量不匹配。"); } if(frontIsOperator){ throw new GrammaticalErrorException("表达式末尾不能是操作符"); } if(operatorCount==0){ throw new GrammaticalErrorException("表达式不能没有操作符"); } return new PriorityExpression().interpreter(computer); }}6.定义优先级 Expression 对字符串进行优先级排序。
import java.util.ArrayList;import java.util.List;
public class PriorityExpression implements IExpression{ @Override public String interpreter(Computer computer) { String str = computer.getOriginStr(); //循环处理含有 (,) 运算符的表达式,直至不包含 (,) 运算符 while(str.contains("(")){ StringBuilder sb = new StringBuilder(str); int firstIndexOfRightBracket = str.indexOf(")"); String substring = str.substring(0, firstIndexOfRightBracket); int lastIndexOfLeftBracket = substring.lastIndexOf("("); String oneCalculate = sb.substring(lastIndexOfLeftBracket + 1, firstIndexOfRightBracket); String afterCalculate = computeWithOutBracket(oneCalculate,computer); StringBuilder beforeSb = new StringBuilder(sb.substring(0, lastIndexOfLeftBracket)); StringBuilder afterInterpreterSb = beforeSb.append(afterCalculate).append(sb.substring((firstIndexOfRightBracket + 1), sb.length())); str = afterInterpreterSb.toString(); } return computeWithOutBracket(str,computer); }
/** * 返回从左至右,stringBuffer中operators中元素第一次出现的index * 如果stringBuffer中不含有任何operators中的元素则返回-1 * @param stringBuffer * @param operators */ private Integer indexPriorOperator(StringBuffer stringBuffer, List<String> operators){ Integer index = -1; for(String operator:operators){ int i = stringBuffer.indexOf(operator); if(i!=-1){ if(index.equals(-1)){ index = i; }else { index = i<index ? i:index; } } } return index; }
/** * 获取str的双向迭代器 * @param str */ public Iterator<Character> getStrIterator(String str) { char[] chars = str.toCharArray(); List<Character> charList= new ArrayList<>(); for(char c:chars){ charList.add(c); } return new GeneralIterator<>(charList); }
/** * 计算不包含 '(' , ')' 的表达式 * @param str * @param computer */ private String computeWithOutBracket(String str,Computer computer){ ComputeExpression computeExpression = new ComputeExpression(); StringBuffer sb = new StringBuffer(str); ArrayList<String> priorOperatorList = new ArrayList<String>() {{ add("*"); add("/"); }}; ArrayList<String> nonPriorOperatorList = new ArrayList<String>() {{ add("+"); add("-"); }}; Integer priorOperatorIndex= indexPriorOperator(sb,priorOperatorList); // 循环处理含有 *,/ 运算符的表达式,直至不包含 *,/ 运算符 while(!priorOperatorIndex.equals(-1)){ String operator = String.valueOf(str.charAt(priorOperatorIndex)); computer.setOperator(operator); String frontSubString = str.substring(0, priorOperatorIndex); Iterator<Character> frontSubStringIterator = getStrIterator(frontSubString); frontSubStringIterator.getEnd(); int leftOperatorNumberIndex = 0; while(frontSubStringIterator.hasPrevious()){ Character previousChar = frontSubStringIterator.getPrevious(); if(previousChar.equals('+') || previousChar.equals('-')){ leftOperatorNumberIndex = frontSubStringIterator.getCurrentIndex()+1; break; } } computer.setLeftOperateNumber(frontSubString.substring(leftOperatorNumberIndex)); String backSubString = str.substring(priorOperatorIndex+1); Iterator<Character> backSubStringIterator = getStrIterator(backSubString); backSubStringIterator.getFirst(); int rightOperatorNumIndex = backSubString.length()-1; while (backSubStringIterator.hasNext()){ Character nextChar = backSubStringIterator.getNext(); if(nextChar.equals('+') || nextChar.equals('-') || nextChar.equals('*') || nextChar.equals('/')){ rightOperatorNumIndex = backSubStringIterator.getCurrentIndex()-1; break; } } computer.setRightOperateNumber(backSubString.substring(0,rightOperatorNumIndex+1)); StringBuilder frontSb = new StringBuilder(str.substring(0,leftOperatorNumberIndex)); String backSb = backSubString.substring(rightOperatorNumIndex+1); str = frontSb.append(computeExpression.interpreter(computer)).append(backSb).toString(); sb = new StringBuffer(str); priorOperatorIndex= indexPriorOperator(sb,priorOperatorList); } Integer nonPriorOperatorIndex = indexPriorOperator(sb,nonPriorOperatorList); // 循环处理含有 +,- 运算符的表达式,直至不包含+,-运算符 while(!nonPriorOperatorIndex.equals(-1)){ computer.setLeftOperateNumber(str.substring(0,nonPriorOperatorIndex)); String operator = String.valueOf(str.charAt(nonPriorOperatorIndex)); computer.setOperator(operator); String backSubStr = str.substring(nonPriorOperatorIndex+1); Iterator<Character> backSubStrIterator = getStrIterator(backSubStr); backSubStrIterator.getFirst(); int rightOperatorNumIndex = backSubStr.length()-1; while (backSubStrIterator.hasNext()){ Character nextChar = backSubStrIterator.getNext(); if(nextChar.equals('+') || nextChar.equals('-')){ rightOperatorNumIndex = backSubStrIterator.getCurrentIndex()-1; break; } } computer.setRightOperateNumber(backSubStr.substring(0,rightOperatorNumIndex+1)); str = computeExpression.interpreter(computer)+(backSubStr.substring(rightOperatorNumIndex+1)); sb = new StringBuffer(str); nonPriorOperatorIndex = indexPriorOperator(sb,nonPriorOperatorList); } return str; }}7.定义计算 Expression 对二元运算进行计算。
public class ComputeExpression implements IExpression{ /** * 计算 二元运算 * @param computer * @return */ @Override public String interpreter(Computer computer) { Integer leftInteger = Integer.valueOf(computer.getLeftOperateNumber()); Integer rightInteger = Integer.valueOf(computer.getRightOperateNumber()); String operator = computer.getOperator(); switch (operator){ case "+": return String.valueOf(leftInteger+rightInteger); case "-": return String.valueOf(leftInteger-rightInteger); case "*": return String.valueOf(leftInteger*rightInteger); case "/": return String.valueOf(leftInteger/rightInteger); default: throw new GrammaticalErrorException("表达式含有非法操作服。"); }
}}8.测试代码
public class InterpreterPattern { public static void main(String[] args) { Computer computer = new Computer("3*2+(2*(3+4))*3/2+(2*2)-7"); GrammarExpression grammarExpression = new GrammarExpression(); System.out.println(grammarExpression.interpreter(computer)); }}总结:
由测试结果可见:
解释器模式对于规律性比较强且有严格规定的业务场景很合适。
解释器模式使用到了递归和循环,执行效率比较低。
版权声明: 本文为 InfoQ 作者【下雨了】的原创文章。
原文链接:【http://xie.infoq.cn/article/8a80cab94dd96e823cdc3e530】。文章转载请联系作者。
下雨了
还未添加个人签名 2021.05.11 加入
还未添加个人简介











评论