在JAVA开发中,程序员有时会遇到这样的情况:其他模块相对稳定,但计算模块经常发生无法预知的变化,每次需要重新编写。比如算法要随业务规则发生变动、数据源种类和库表结构要适应具体应用环境、查询条件要根据客户的需求相应调整等。这类需求可以用动态可挂接计算模块实现,以便在不影响其他功能的情况下动态替换原有的计算模块。下面是一些常见的例子:
1.同样的查询功能,不同的客户对查询条件的要求不同,需要根据客户要求来定制计算模块。
2.从Excel文件中读数并计算,参数输入界面和结果输出界面不变,但需要根据Excel的格式来动态使用不同的算法。
3.各分支机构有自己的数据库,需要从不同类型、不同结构的库表中呈现相同的查询结果。
4. 某企业员工的实际工资是通过绩效得分计算出的,算法经常变动,需要在不改动其他代码的情况下用新算法替换旧算法。
实现动态可挂接计算模块的方法很多,这里介绍如何用集算器实现第4个例子。
首先,用集算器写出计算模块,其脚本如下:
算法不是本文重点,输入输出部分更加重要。从A1单元格可以看出,该模块有两个输入参数:start、end,它们来自JAVA调用。从A12单元格可以看出,计算结果会以JDBC的形式输出给JAVA。
接着,在JAVA中实现动态模块的调用代码:
Class.forName(“com.esproc.jdbc.InternalDriver”).newInstance();
Connection conn = DriverManager.getConnection(“jdbc:esProc:local://”);
CallableStatement cstmt = conn.prepareCall(“call dynamic(?,?)”);
cstmt.setString(1, start);
cstmt.setString(2, end);
cstmt.execute();
ResultSet rs = cstmt.getResultSet();
可以看到,调用集算器脚本和调用普通数据库的存储过程完全一,Driver Name和URL要遵循集算器的命名规则。dynamic是模块名,即集算器脚本文件名dynamic.dfx的前缀,dynamic有两个输入参数start、end。计算结果存储在rs变量中,可以直接输出到网页或前台界面中。
最后,动态挂接。
算法改变时需要用新模块替换旧模块,从上一步的JAVA代码中可以看出,计算模块和JAVA代码的耦合性很低,只需要用同名文件直接替换就行。如果想保留旧模块,也可以从配置文件读取模块名,或者将模块名通过字符串参数传给prepareCall函数。具体的新模块代码并非本文重点,这里就不列出了。
通过上面几步,集算器轻松实现了新旧计算模块的替换。
当然,全部使用JAVA代码也可以实现动态可挂接计算模块,但JAVA缺乏基础算法类库,比如:分组、汇总、排序、过滤、关联、唯一值、交集、排名等等,程序员必须手工编写这些基础算法才能实现计算模块。将这些基础算法直接实现在业务逻辑中显然是不合理的,这会导致每个计算模块重复书写类似的代码,计算模块过于庞大、可读性变差。理想的作法是先实现一套基础算法类库,再在计算模块中调用这些类库,但应用程序员很难设计出完备性和系统性优秀的基础算法类库,常常使代码的耦合性高,稳定性差,最终导致计算模块维护困难。另外,JAVA代码需要重新编译,部署起来比较麻烦;当可挂接的计算模块较多时,不论是否还要使用它们,这些Java class/jar都会占据内存空间而无法释放,对性能有一定的影响。
集算器本身就有一套精心设计的完备基础类库,只需要JAVA十分之一的代码量就可以实现同样功能的计算模块,开发效率更高;集算器的主程序和脚本文件是分开的,耦合性很低,维护起来更加方便。另外,集算器脚本无需编译即可使用,是真正的热部署。集算器的脚本程序不会事先加载到内存,而是使用时再加载,计算完立刻释放,不会长期占用内存。