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


参考





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值