设计模式之解释器模式
解释器模式:是一种按照规定语法进行解析的方案,在现在项目中使用较少 ,给定一门语言,定义它的规则的一种表达式,并定义一个解释器,该解释器使用该表达式来解释语言中的句子。
用的比较少,了解即可
2.解释器模式例子:
例子:输入一个模型公式(加、减运算),然后输入模型中的参数,运算出结果。
3.代码
3.1 创建一个Expression表达式类
public abstract class Expression { /** * 抽象表达式,用来解析表达式的值 * map的key表示具体的变量。value表示变量的值 * * @param map * @return */ public abstract int interpreter(Map<String, Integer> map); }
3.2 创建一个变量解析器
public class VarExpression extends Expression { private String key; public VarExpression(String key) { this.key = key; } // 获取具体变量的值 @Override public int interpreter(Map<String, Integer> map) { return map.get(key); } }
3.3 抽象运算符号解析器
public abstract class SymbolExpression extends Expression { protected Expression left; protected Expression right; public SymbolExpression(Expression left, Expression right) { System.out.println("调用了symbolExpression中的方法=========>left " + left + "=========>" + "right: " + right + " "); this.left = left; this.right = right; } }
3.4 加法解析器
public class AddExpression extends SymbolExpression { public AddExpression(Expression left, Expression right) { super(left, right); } @Override public int interpreter(Map<String, Integer> map) { int result = super.left.interpreter(map) + super.right.interpreter(map); System.out.println(" add中的result============>" + result + " "); return result; } }
3.5 减法解析器
public class SubExpression extends SymbolExpression { public SubExpression(Expression left, Expression right) { super(left, right); } @Override public int interpreter(Map<String, Integer> map) { int subresult = super.left.interpreter(map) - super.right.interpreter(map); System.out.println(" sub计算结果=======>" + subresult + " "); return subresult; } }
3.6 创建解析器封装器
public class Calculator { private Expression expression; public Calculator(String expStr) { // 定义一个栈,安排运行的先后执行 Stack<Expression> stack = new Stack<>(); // 表达式拆分为字节数据 char[] charArray = expStr.toCharArray(); Expression left; Expression right; for (int i = 0; i < charArray.length; i++) { switch (charArray[i]) { case '+': left = stack.pop(); right = new VarExpression(String.valueOf(charArray[++i])); stack.push(new AddExpression(left, right)); System.out.println("left: " + left + " right==>" + right + " stack中的值======>" + stack.toString()); break; case '-': left = stack.pop(); right = new VarExpression(String.valueOf(charArray[++i])); stack.push(new SubExpression(left, right)); System.out.println("left: " + left + " right==>" + right + " stack中的值======>" + stack.toString()); break; default: // 公式中的变量 stack.push(new VarExpression(String.valueOf(charArray[i]))); System.out.println("stack中的值======>" + stack.toString()); } } // 把运算结果抛出来 this.expression = stack.pop(); } // 开始运算 public int run(HashMap<String, Integer> map) { return this.expression.interpreter(map); } }
3.7 客户端代码
public class Client { public static void main(String[] args) throws Exception { String expStr = getExpStr(); Calculator calculator = new Calculator(expStr); // 赋值 HashMap<String, Integer> var = getVar(expStr); System.out.println("运算结果为:" + expStr + "=" + calculator.run(var)); } private static HashMap<String, Integer> getVar(String expStr) throws Exception { HashMap<String, Integer> hashMap = new HashMap<String, Integer>(); // 解析几个参数要传递 for (char ch : expStr.toCharArray()) { if (ch != '+' && ch != '-') { // 解决重复参数的问题 if (!hashMap.containsKey(String.valueOf(ch))) { System.out.println("请输入变量" + ch + "的值: "); String in = (new BufferedReader(new InputStreamReader(System.in))).readLine(); hashMap.put(String.valueOf(ch), Integer.valueOf(in)); } } } return hashMap; } private static String getExpStr() throws IOException { System.out.println("请输入表达式"); return (new BufferedReader(new InputStreamReader(System.in))).readLine(); } }
运行截图:
4.解释器的优点:
解释器是一个简单语法分析工具,它最显著的优点就是扩展性,修改语法规则只要修改相应的表达式就可以了,若扩展语法,则只要增加规则类就可以了,例如add,sub。
5.解释器的缺点
- 解释器模式会引起类膨胀:一个规则加一个类
- 解释器模式采用递归调用方法:如果程序报错了,一步步调试下去,非常复杂
- 效率问题:解释器模式由于使用了大量的循环和递归,效率是一个不容忽视的问题,特别是一用于解析复杂、冗长的语法时,效率是难以忍受的。
6.解释器的应用场景
- 重复发生的问题可以使用解释器模式
- 一个简单语法需要解释的场景
7.解释器模式注意事项
尽量不要在重要的模块中使用解释器模式,否则维护会是一个很大的问题。在项目中可以使用shell、JRuby、Groovy等脚本语言来代替解释器模式,弥补Java编译型语言的不足。我所在公司是金融证券公司,涉及到很多业务类型的计算场景。我们的计算组件采用的时候MVEL表达式的方式进行计算,使用起来美滋滋的。准备使用解释器模式的时候,可以百度搜一下Expression4J、MESP(Math Expression String Parser)、Jep等开源的解析工具包。功能都异常强大,而且非常容易使用,效率也还不错,实现大多数的数学运算完全没有问题。总之,就是有大佬开发的现成的工具,直接拿来用就好了