23种设计模式(14):解释器模式
类图
场景
在银行、证券类项目中,经常会有一些模型运算,通过对现有数据的统计、分析而预测不可知或未来可能发生的商业行为。模型运算大部分是针对海量数据的,例如建立一个模型公式,分析一个城市的消费倾向,进而影响银行的营销和业务扩张方向,一般的模型运算都有一个或多个运算公式,通常是加减乘除四则运算,偶尔也有指数、开方等复杂运算。具体到一个金融业务中,模型公式是非常复杂的,虽然只有加减乘除四则运算,但是公式有可能有十多个参数,而且上百个业务品各有不同的取参路径,同时相关表的数据量都在百万级,呵呵,复杂了吧,不复杂那就不叫金融业务,我们就来讲讲运算的核心——模型公式,如何实现。
业务需求:输入一个模型公式(加减四则运算),然后输入模型中的参数,运算出结果。
设计要求:
•公式可以运行期编辑,并且符合正常算术书写方式,例如a+b-c;
•高扩展性,未来增加指数、开方、极限、求导等运算符号时,较少改动量;
•效率可以不用考虑,晚间批量运算。
需求不复杂,若仅仅对数字采用四则运算,每个程序员都可以写出来。但是增加了增加模型公式就复杂了。先解释一下为什么需要公式, 而不采用直接计算的方法,例如有如下3个公式:
•业务种类1的公式:a+b+c-d;
•业务种类2的公式:a+b+e-d;
•业务种类3的公式:a-f。
其中,a、b、c、d、e、f参数的值都可以取得,如果使用直接计算数值的方法需要为每个品种写一个算法,目前仅仅是3个业务种类,那上百个品种呢?歇菜了吧!建立公式,然后通过公式运算才是王道。
我们以实现加减算法(由于篇幅所限,乘除法的运算读者可以自行扩展)的公式为例,讲解如何解析一个固定语法逻辑。由于使用语法解析的场景比较少,而且一些商业公司(比如SAS、SPSS等统计分析软件)都支持类似的规则运算,亲自编写语法解析的工作已经非常少,以下例程采用逐步分析方法,带领大家了解这一实现过程。
我们来想,公式中有什么?仅有两类元素:运算元素和运算符号,运算元素就是指a、b、c等符号,需要具体赋值的对象,也叫做终结符号,为什么叫终结符号呢?因为这些元素除了需要赋值外,不需要做任何处理,所有运算元素都对应一个具体的业务参数,这是语法中最小的单元逻辑,不可再拆分;运算符号就是加减符号,需要我们编写算法进行处理,每个运算符号都要对应处理单元,否则公式无法运行,运算符号也叫做非终结符号。两类元素的共同点是都要被解析,不同点是所有的运算元素具有相同的功能,可以用一个类表示,而运算符号则是需要分别进行解释,加法需要加法解析器,减法也需要减法解析器。
场景类图
JAVA代码
public abstract class Expression { //解析公式和数值,其中var中的key值是是公式中的参数,value值是具体的数字 public abstract int interpreter(HashMap<String,Integer> var); }
public class VarExpression extends Expression { private String key; public VarExpression(String _key){ this.key = _key; } //从map中取之 public int interpreter(HashMap<String, Integer> var) { return var.get(this.key); } }
public class VarExpression extends Expression { private String key; public VarExpression(String _key){ this.key = _key; } //从map中取之 public int interpreter(HashMap<String, Integer> var) { return var.get(this.key); } }
public abstract class SymbolExpression extends Expression { protected Expression left; protected Expression right; //所有的解析公式都应只关心自己左右两个表达式的结果 public SymbolExpression(Expression _left,Expression _right){ this.left = _left; this.right = _right; } }
public class AddExpression extends SymbolExpression { public AddExpression(Expression _left,Expression _right){ super(_left,_right); } //把左右两个表达式运算的结果加起来 public int interpreter(HashMap<String, Integer> var) { return super.left.interpreter(var) + super.right.interpreter(var); } } 代码清单27-5 减法解析器 public class SubExpression extends SymbolExpression { public SubExpression(Expression _left,Expression _right){ super(_left,_right); } //左右两个表达式相减 public int interpreter(HashMap<String, Integer> var) { return super.left.interpreter(var) - super.right.interpreter(var); } }
public class Calculator { //定义的表达式 private Expression expression; //构造函数传参,并解析 public Calculator(String expStr){ //定义一个堆栈,安排运算的先后顺序 Stack<Expression> stack = new Stack<Expression>(); //表达式拆分为字符数组 char[] charArray = expStr.toCharArray(); //运算 Expression left = null; Expression right = null; 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)); break; case '-': left = stack.pop(); right = new VarExpression(String.valueOf(charArray[++i])); stack.push(new SubExpression(left,right)); break; default: //公式中的变量 stack.push(new VarExpression(String.valueOf(charArray[i]))); } } //把运算结果抛出来 this.expression = stack.pop(); } //开始运算 public int run(HashMap<String,Integer> var){ return this.expression.interpreter(var); } }
public class Client { //运行四则运算 public static void main(String[] args) throws IOException{ String expStr = getExpStr(); //赋值 HashMap<String,Integer> var = getValue(expStr); Calculator cal = new Calculator(expStr); System.out.println("运算结果为:"+expStr +"="+cal.run(var)); } //获得表达式 public static String getExpStr() throws IOException{ System.out.print("请输入表达式:"); return (new BufferedReader(new InputStreamReader(System.in))).readLine(); } //获得值映射 public static HashMap<String,Integer> getValue(String exprStr) throws IOException{ HashMap<String,Integer> map = new HashMap<String,Integer>(); //解析有几个参数要传递 for(char ch:exprStr.toCharArray()){ if(ch != '+' && ch != '-'){ //解决重复参数的问题 if(!map.containsKey(String.valueOf(ch))){ System.out.print("请输入"+ch+"的值:"); String in = (new BufferedReader(new InputStreamReader(System.in))).readLine(); map.put(String.valueOf(ch),Integer.valueOf(in)); } } } return map; } }
其中,getExpStr是从键盘事件中获得的表达式,getValue方法是从键盘事件中获得表达式中的元素映射值,运行过程如下。
首先,要求输入公式。
请输入表达式:a+b-c
其次,要求输入公式中的参数。
请输入a的值:100
请输入b的值:20
请输入c的值:40
最后,运行出结果。
运算结果为:a+b-c=80
参考
https://www.geek-share.com/detail/2472014120.html
- 23种设计模式(14):解释器模式
- 23种设计模式(14):解释器模式
- 23种设计模式(14):解释器模式
- 23种设计模式(14):解释器模式
- 23种设计模式-14-解释器模式
- 23种设计模式(14):解释器模式
- 23种设计模式(14):解释器模式
- 23种设计模式(14):解释器模式
- 23种设计模式(14):解释器模式
- 23种设计模式(14):解释器模式
- 23种设计模式(14):解释器模式
- 设计模式(二十二)------23种设计模式(14):解释器模式
- 23种设计模式(14):解释器模式
- 23种设计模式(14):解释器模式
- 23种设计模式(14):解释器模式
- 23种设计模式(19)_行为型_解释器模式(Interpreter Pattern)
- Java开发中的23种设计模式详解----解释器模式(Interpreter)
- 走穿java23种设计模式--14命令模式详解
- 23种设计模式14---访问者模式
- 23种设计模式(14)-迭代器模式