"代码微变,质变!优雅编程的炼金术"
一次需求开发时碰到如下所示方法代码:
private OrderShoudSettlementAmount getOrderShoudSettlementAmount(OrderDTO orderMain, List<SettlementDetail> details) {OrderShoudSettlementAmount settlementAmount = new OrderShoudSettlementAmount();// 应结金额=33021-33002-32003+32001-31001// 货款佣金=33005+33002+32003+31001long feeMoney33021 = 0;long feeMoney33002 = 0;long feeMoney32003 = 0;long feeMoney32001 = 0;long feeMoney31001 = 0;long feeMoney33005 = 0;for (SettlementDetail settlementDetail : details) {if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_FREIGHT_ZS_NOSETTLE.getVal())) {feeMoney33021 += settlementDetail.getOassMoney();}if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_FREIGHT_YJ_ZS_NOSETTLE.getVal())) {feeMoney33002 += settlementDetail.getOassMoney();}if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_GOODS_YJ_WJTZ.getVal())) {feeMoney32003 += settlementDetail.getOassMoney();}if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_GOODS_NOSETTLE.getVal())) {feeMoney32001 += settlementDetail.getOassMoney();}if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_BDYJ_YJ_NOSETTLE.getVal())) {feeMoney31001 += settlementDetail.getOassMoney();}if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_YFYJ_ZS_XSG.getVal())) {feeMoney33005 += settlementDetail.getOassMoney();}}long settlementMoney = feeMoney33021 - feeMoney33002 - feeMoney32003 + feeMoney32001 - feeMoney31001;long goodCommissionMoney = feeMoney33005 + feeMoney33002 + feeMoney32003 + feeMoney31001;settlementAmount.setSettlementAmount(settlementMoney);settlementAmount.setGoodsCommission(goodCommissionMoney);settlementAmount.setOrderId(orderMain.getOrderId());settlementAmount.setOrgCode(orderMain.getOrgCode());settlementAmount.setStationNo(String.valueOf(orderMain.getDeliveryStationNo()));settlementAmount.setBillTime(new Date());settlementAmount.setRetSuccess(false);return settlementAmount;}
该方法逻辑比较简单,就是组装OrderShoudSettlementAmount对象。其中需要计算2个金额,分别是settlementMoney和goodCommissionMoney。
private OrderShoudSettlementAmount getOrderShoudSettlementAmount(OrderDTO orderMain, List<SettlementDetail> details) {OrderShoudSettlementAmount settlementAmount = new OrderShoudSettlementAmount();// 应结金额=33021-33002-32003+32001-31001+34012-34013// 货款佣金=33005+33002+32003+31001+34013long feeMoney33021 = 0;long feeMoney33002 = 0;long feeMoney32003 = 0;long feeMoney32001 = 0;long feeMoney31001 = 0;long feeMoney33005 = 0;// 本次需求新增费项long feeMoney34012 = 0;// 本次需求新增费项long feeMoney34013 = 0;for (SettlementDetail settlementDetail : details) {if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_FREIGHT_ZS_NOSETTLE.getVal())) {feeMoney33021 += settlementDetail.getOassMoney();}if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_FREIGHT_YJ_ZS_NOSETTLE.getVal())) {feeMoney33002 += settlementDetail.getOassMoney();}if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_GOODS_YJ_WJTZ.getVal())) {feeMoney32003 += settlementDetail.getOassMoney();}if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_GOODS_NOSETTLE.getVal())) {feeMoney32001 += settlementDetail.getOassMoney();}if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_BDYJ_YJ_NOSETTLE.getVal())) {feeMoney31001 += settlementDetail.getOassMoney();}if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_YFYJ_ZS_XSG.getVal())) {feeMoney33005 += settlementDetail.getOassMoney();}// 本次需求新增费项if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_CHF_XSG_NOSETTLE.getVal())) {feeMoney34012 += settlementDetail.getOassMoney();}// 本次需求新增费项if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_CHF_YJ_XSG.getVal())) {feeMoney34013 += settlementDetail.getOassMoney();}}// 本次需求新增费项追加计算 + feeMoney34012 - feeMoney34013long settlementMoney = feeMoney33021 - feeMoney33002 - feeMoney32003 + feeMoney32001 - feeMoney31001 + feeMoney34012 - feeMoney34013;// 本次需求新增费项追加计算 + feeMoney34013long goodCommissionMoney = feeMoney33005 + feeMoney33002 + feeMoney32003 + feeMoney31001 + feeMoney34013;settlementAmount.setSettlementAmount(settlementMoney);settlementAmount.setGoodsCommission(goodCommissionMoney);settlementAmount.setOrderId(orderMain.getOrderId());settlementAmount.setOrgCode(orderMain.getOrgCode());settlementAmount.setStationNo(String.valueOf(orderMain.getDeliveryStationNo()));settlementAmount.setBillTime(new Date());settlementAmount.setRetSuccess(false);return settlementAmount;}
Martin Fowler在《重构:改善既有代码的设计》一书中列出了22种代码的坏味道:
参照这22种代码的坏味道,我在以上方法代码中嗅出了2种代码的坏味道:
【 坏味道1:Duplicated Code(重复的代码)】
for循环中对每种费项的累加操作是重复代码,而且每次新增费项,还得不断增加该重复操作。
for (SettlementDetail settlementDetail : details) {if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_FREIGHT_ZS_NOSETTLE.getVal())) {feeMoney33021 += settlementDetail.getOassMoney();}if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_FREIGHT_YJ_ZS_NOSETTLE.getVal())) {feeMoney33002 += settlementDetail.getOassMoney();}if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_GOODS_YJ_WJTZ.getVal())) {feeMoney32003 += settlementDetail.getOassMoney();}if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_GOODS_NOSETTLE.getVal())) {feeMoney32001 += settlementDetail.getOassMoney();}if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_BDYJ_YJ_NOSETTLE.getVal())) {feeMoney31001 += settlementDetail.getOassMoney();}if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_YFYJ_ZS_XSG.getVal())) {feeMoney33005 += settlementDetail.getOassMoney();}// 本次需求新增费项if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_CHF_XSG_NOSETTLE.getVal())) {feeMoney34012 += settlementDetail.getOassMoney();}// 本次需求新增费项if (settlementDetail.getExpenseType().equals(FeeInfoEnum.FEE_INFO_CHF_YJ_XSG.getVal())) {feeMoney34013 += settlementDetail.getOassMoney();}}
针对前面嗅出的代码坏味道,果断进行重构。重构之后代码如下所示:
private OrderShoudSettlementAmount getOrderShoudSettlementAmount(OrderDTO orderMain, List<SettlementDetail> details) {OrderShoudSettlementAmount settlementAmount = new OrderShoudSettlementAmount();Map<Integer, Long> expenseTypeToFeeMoneyMap = Maps.newHashMap();for (SettlementDetail settlementDetail : details) {long feeMoney = Optional.ofNullable(expenseTypeToFeeMoneyMap.get(settlementDetail.getExpenseType())).orElse(0L);feeMoney += Optional.ofNullable(settlementDetail.getOassMoney()).orElse(0L);expenseTypeToFeeMoneyMap.put(settlementDetail.getExpenseType(), feeMoney);}long settlementMoney = SettlementMoneyCalcFeeInfoEnum.calcSettlementMoney(expenseTypeToFeeMoneyMap);long goodCommissionMoney = GoodCommissionMoneyCalcFeeInfoEnum.calcGoodCommissionMoney(expenseTypeToFeeMoneyMap);settlementAmount.setSettlementAmount(settlementMoney);settlementAmount.setGoodsCommission(goodCommissionMoney);settlementAmount.setOrderId(orderMain.getOrderId());settlementAmount.setOrgCode(orderMain.getOrgCode());settlementAmount.setStationNo(String.valueOf(orderMain.getDeliveryStationNo()));settlementAmount.setBillTime(new Date());settlementAmount.setRetSuccess(false);return settlementAmount;}
enum SettlementMoneyCalcFeeInfoEnum {/**计算项*/FEE_33021(FeeInfoEnum.FEE_INFO_FREIGHT_ZS_NOSETTLE, "+"),FEE_33002(FeeInfoEnum.FEE_INFO_FREIGHT_YJ_ZS_NOSETTLE, "-"),FEE_32003(FeeInfoEnum.FEE_INFO_GOODS_YJ_WJTZ, "-"),FEE_32001(FeeInfoEnum.FEE_INFO_GOODS_NOSETTLE, "+"),FEE_31001(FeeInfoEnum.FEE_INFO_BDYJ_YJ_NOSETTLE, "-"),FEE_34012(FeeInfoEnum.FEE_INFO_CHF_XSG_NOSETTLE, "+"),FEE_34013(FeeInfoEnum.FEE_INFO_CHF_YJ_XSG, "-");private final FeeInfoEnum feeInfoEnum;private final String symbol;SettlementMoneyCalcFeeInfoEnum(FeeInfoEnum feeInfoEnum, String symbol) {this.feeInfoEnum = feeInfoEnum;this.symbol = symbol;}public static long calcSettlementMoney(Map<Integer, Long> expenseTypeToFeeMoneyMap) {// 应结金额=33021-33002-32003+32001-31001+34012-34013long settlementMoney = 0L;for (SettlementMoneyCalcFeeInfoEnum calcFeeInfoEnum : SettlementMoneyCalcFeeInfoEnum.values()) {if ("+".equals(calcFeeInfoEnum.symbol)) {settlementMoney += Optional.ofNullable(expenseTypeToFeeMoneyMap.get(calcFeeInfoEnum.feeInfoEnum.getVal())).orElse(0L);}if ("-".equals(calcFeeInfoEnum.symbol)) {settlementMoney -= Optional.ofNullable(expenseTypeToFeeMoneyMap.get(calcFeeInfoEnum.feeInfoEnum.getVal())).orElse(0L);}}return settlementMoney;}}
enum GoodCommissionMoneyCalcFeeInfoEnum {/**计算项*/FEE_33005(FeeInfoEnum.FEE_INFO_YFYJ_ZS_XSG),FEE_33002(FeeInfoEnum.FEE_INFO_FREIGHT_YJ_ZS_NOSETTLE),FEE_32003(FeeInfoEnum.FEE_INFO_GOODS_YJ_WJTZ),FEE_31001(FeeInfoEnum.FEE_INFO_BDYJ_YJ_NOSETTLE),FEE_34013(FeeInfoEnum.FEE_INFO_CHF_YJ_XSG);private final FeeInfoEnum feeInfoEnum;GoodCommissionMoneyCalcFeeInfoEnum(FeeInfoEnum feeInfoEnum) {this.feeInfoEnum = feeInfoEnum;}public static long calcGoodCommissionMoney(Map<Integer, Long> expenseTypeToFeeMoneyMap) {// 货款佣金=33005+33002+32003+31001+34013long goodCommissionMoney = 0L;for (GoodCommissionMoneyCalcFeeInfoEnum calcFeeInfoEnum : GoodCommissionMoneyCalcFeeInfoEnum.values()) {goodCommissionMoney += Optional.ofNullable(expenseTypeToFeeMoneyMap.get(calcFeeInfoEnum.feeInfoEnum.getVal())).orElse(0L);}return goodCommissionMoney;}}
以上重构的方法代码比较简单,有些人可能会觉得不重构也挺好的,代码可读性也不差,每次修改也就肉眼可见的几个地方,没必要在这上面花费时间。