某天,领导说重构系统,不出意外这是第4次重构系统了,当然去年就说了,今年年底才紧锣密鼓的执行。
而我现在所在的公司,经常调整计算公式,我的后端开发们天天写死计算公式在代码里面,虽然我只负责前端开发,依然在想提高前端的开发速度,代码至简,简单的代码实现复杂的功能。
当然这篇文章提出的重点就是围绕上面提出写死计算公式业务展开讨论与提出的计算公式模板实际方案是否可行。
我从后端提取一段平时写的冰山一角代码,转换为下面的一条公式:
50 * 0.5 + 25 + 150 * 2 + 100 * 0.5
java
// 模拟数据库返回
@Data
class KfcVMe50 {
private BigDecimal money1; // 50
private BigDecimal money2; // 25
private BigDecimal money3; // 150
private BigDecimal money4;// 100
private BigDecimal ratio; //0.5
private BigDecimal fold; //0.5
private BigDecimal sum;
public void getKfc(){
this.setMoney1(new BigDecimal("50"));
this.setMoney2(new BigDecimal("25"));
this.setMoney3(new BigDecimal("150"));
this.setMoney4(new BigDecimal("100"));
this.setRatio(new BigDecimal("0.5"));
this.setFold(new BigDecimal("2"));
}
}
java
// 模拟计算逻辑
@Test
void sum (){
var money = new KfcVMe50();
money.getKfc();
money.setSum(
money.getMoney1()
.multiply(money.getRatio())
.add(money.getMoney2())
.add(money.getMoney3())
.multiply(money.getFold())
.add(money.getMoney4())
.multiply(money.getRatio())
);
System.out.println(money);
System.out.println(execute("50*0.5+25+150*2+100*0.5"));
System.out.println(
BigDecimal.valueOf(50)
.multiply(BigDecimal.valueOf(0.5)) // 50 * 0.5 = 25
.add(BigDecimal.valueOf(25)) // 25 + 25 = 50
.add(BigDecimal.valueOf(150)) //50 + 150 = 200
.multiply(BigDecimal.valueOf(2)) //200 * 2 = 400
.add(BigDecimal.valueOf(100)) // 400 + 100 = 500
.multiply(BigDecimal.valueOf(0.5)) //500 * 0.5
);
}
得出结果
有经验的开发者已经有经验了 ,假如某一天修改公式和添加一个字段,怎么办 (难办,那就不办了) ,有开发经验的你当然是继续增加或修改代码了,这简直是灾难性的维护...
接下来根据我提出的计算公式模板验证这个问题是否可行,根据上面的代码我们可以知道转换后的公式,先定义一个字符串公式
java
//50*0.5+25+150*2+100*0.5
String nlms = "( ( ( ( money1 * ratio ) + money2 ) + money3 ) * fold + money4 ) * ratio ";
java
@Test
void templateSum() {
var kfcVMe50 = new KfcVMe50();
kfcVMe50.getKfc();
var map = (Map<String, Object>) JSON.toJSON(kfcVMe50);
System.out.println("原来的计算模板:" + nlms);
for (String key : map.keySet()) if (null != map.get(key)) nlms = nlms.replace(key.toString(), map.get(key).toString() ).toString();
System.out.println("公式替换后:"+nlms);
System.out.println("获取到公式计算结果: " + execute(nlms));
kfcVMe50.setSum(execute(nlms));
System.out.println("map=======>" + map);
System.out.println(JSON.toJSONString(kfcVMe50));
}
BigDecimal execute( String resultNlms ){
JEP jep = new JEP();
jep.parseExpression(resultNlms); // 解析公式
BigDecimal result = BigDecimal.valueOf(jep.getValue()); // 获取结果
return result;
}
我们直接看结果
其实并不复杂,核心就是在这里,先把实体类转换为map,在遍历map替换公式里面的字符串
java
var map = (Map<String, Object>) JSON.toJSON(kfcVMe50);
for (String key : map.keySet()) if (null != map.get(key)) nlms = nlms.replace(key.toString(), map.get(key).toString() ).toString();
把替换好的字符串公式,使用 JEP 库执行计算公式,至此,以后的修改公式操作,我们只需要简单的维护计算公式模板,而不是代码本身,达到可扩展和维护简单的效果,甚至计算公式可交由业务人员维护,但是这些都是后面的事情了,和我又有什么关系呢
我只是看到又是一场重蹈覆辙的轮回而在感慨写这一篇文章罢了,这不一定是最优的写法,但是确是一种灵活解决写死代码的方法,但是可以根据当前代码设计思想(灵魂)深入写出更好更灵活的bug
当前好的资源在没有分配到自己的时候,平凡牛马耕好自己的一亩三分地,不要越界。 (人话就是,开会不带我,业务不带我,同一个阶级,你花钱让别人干了,还想让我帮他干,你这不是欺负老实人嘛。这不是我负责的事情,得加钱...)