已经是公元2023年了,一定还有这么写代码的Javaer——看如何把简单事情搞复杂的

文章讨论了Java代码中的常见问题,如冗余的if-else结构、魔法数字和不清晰的方法命名。提出了将isTax方法改用布尔属性、将复杂的逻辑移至VO对象以及使用状态模式重构代码的建议。此外,还提到了利用AOP来减少重复的检查代码,以提高代码的简洁性和可维护性。
摘要由CSDN通过智能技术生成

        先来一道开胃菜。这是典型的Javaer对于类似问题的代码写法。 

public class XXService {

    ... 

    private Boolean isTax(PurchaseBillItem purchaseBillItem) {
        if (purchaseBillItem.getIsTax() == null) {
            throw new BizException("是否是税标志isTax不能为空");
        }
        if (purchaseBillItem.getIsTax() == 0) {// 不是税
            return false;
        }
        if (purchaseBillItem.getIsTax() == 1) {//是税
            return true;
        }
        return true;
    }
    
    ...

}

//调用 isTax 

if (!isTax(purchaseBillItem)) {

    ...

}

尝尝这么写香不香。(将is_tax从int改为tinyint(1))
  |
  |
 V

public class PurchaseBillItem {

    ...

    public boolean isTax() {
        return isTax;
    }
    
    ...

}

        再来看一个。

    /**
     * 判断进项税科目
     *
     * @param vo
     * @param adCreateFeeRequestVO
     * @return 0:进项税科目22210113  1:进项税科目22210114   -1:无
     */
    private String purchaseUpdateInputTax(AdCreateRequestVO vo, AdCreateFeeRequestVO adCreateFeeRequestVO) {
        if ("0".equals(vo.getCompanyArea())       //境内
                && "1".equals(vo.getCompanyTaxPayerNature())) { //一般纳税人
            if ("1".equals(vo.getSupplierArea())) {  //供应商为境外
                return "1";
            } else { //供应商为境内
                if ("1".equals(vo.getSupplierTaxPayerNature())) { //供应商为一般纳税人
                    return "0";
                } else if ("2".equals(vo.getSupplierTaxPayerNature())) { //小规模
                    //需判断发票类型
                    if ("1".equals(adCreateFeeRequestVO.getInvoiceType())) { //增值税专用发票
                        return "0";
                    }
                }
            }
        }
        return "-1";
    }

        首先是这个方法名,purchase这个动词说明是跟业务有关系,而且还是一个很大的业务逻辑,而如果真看代码则根本不是那么回事,其实加个get前缀才符合这个方法的气质。其次,宁可用一堆魔数加注释都舍不得定义几个常量。当然,更合适的方式是将这些equals判断放入vo中,然后给方法起一个符合业务的名字,比如 getSubjectOfInputTax()。

        接下来换个口味。

    /**
     * 生成分录明细信息列表
     *
     * @param subjectCode         科目编码
     * @param dcFlag              借贷方向
     * @param adCreateRequestVO   凭证请求对象
     * @param amount              金额
     * @param entrySeq            分录行号
     * @param accountDocumentInfo 凭证信息
     * @param abstrctRule         摘要规则
     * @return
     */
    private List<AdAccountDocumentDetailPO> populateDocumentDetails(String subjectCode, Integer dcFlag, AdCreateRequestVO adCreateRequestVO, BigDecimal amount, int entrySeq, AdAccountDocumentInfoPO accountDocumentInfo, DataDicPO abstrctRule) {
        List<AdAccountDocumentDetailPO> accountDocumentDetails = new ArrayList<AdAccountDocumentDetailPO>();
        AdAccountDocumentDetailPO accountDocumentDetail;
        String remark = "";
        if (AdConstants.SPECIAL_ACC_SUBJECT_CODE_FEES.equals(subjectCode)) { //特殊科目:0
            List<AdCreateFeeRequestVO> fees = this.getFeesForVoucherType(adCreateRequestVO, accountDocumentInfo.getVoucherType());
            if (CollectionUtils.isEmpty(fees)) {
                log.info("凭证生成时,会计科目配置为0,但缺少明细行数据");
                throw new BizException("凭证生成时,缺少明细行数据");
            }
            Map<Long, AdAccountDocumentDetailPO> feeMap = new HashMap<Long, AdAccountDocumentDetailPO>();
            //费用行对应的分录
            for (AdCreateFeeRequestVO fee : fees) {
                BigDecimal feeAmount = fee.getAmount();
                log.info("费用行数据详情:{}", JSON.toJSONString(fee));
                String actualSubjectCode = "";
                if (BillTypeKindConstants.BILL_TYPE_KIND_PURCHASE.equals(adCreateRequestVO.getBillTypeKind())) {
                    Map<String, Object> temp = purchaseHandler(adCreateRequestVO, accountDocumentInfo, fee, dcFlag);
                    feeAmount = (BigDecimal) temp.get("feeAmount");
                    actualSubjectCode = temp.get("actualSubjectCode").toString();
                } else {
                    actualSubjectCode = adAccountRuleService.getSubjectCodeByFee(fee.getFeeItemsCode(), fee.getDeptType(), fee.getAssetFlag());
                    if (StringUtils.isEmpty(actualSubjectCode)) {
                        log.error("特殊科目0处理时,按费用行生成分录时没有找到对应的会计科目,feeItemCode={}, deptType={}, assetFlag={}", fee.getFeeItemsCode(), fee.getDeptType(), fee.getAssetFlag());
                        throw new BizException(MessageFormat.format("费用行[{0}]部门类型[{1}-{2}]未找到对应会计科目"
                                , fee.getFeeItemsCode(), fee.getDeptType(), "10".equals(fee.getDeptType()) ? "管理" : "销售"));
                    }
                }

                //特殊处理参数 借款核销单行上部门取一级部门名称
                if (!StringUtils.isEmpty(fee.getDeptNameLong())) {
                    this.filterRequestParams(accountDocumentInfo.getVoucherType(), adCreateRequestVO, fee, adCreateRequestVO.getDepts());
                }
                //构造摘要信息
                log.info("本次生成摘要===1");
                remark = this.buildAbstract(adCreateRequestVO, fee, abstrctRule);
                accountDocumentDetail = this.adAccountDocumentDetailService.populateDocumentDetail(actualSubjectCode, dcFlag, adCreateRequestVO, feeAmount, entrySeq++, accountDocumentInfo, remark);
                //按费用行生成分录时辅助核算项有可能与行有关
                accountDocumentDetail.setFee(fee);
                accountDocumentDetail.setFeeJson(JSON.toJSONString(fee));
                accountDocumentDetails.add(accountDocumentDetail);

                //应付,id不为空时,记录凭证详情,供后续使用
                if (AmountTypeEnum.BILL_AMOUNT.getVoucherType().equals(accountDocumentInfo.getVoucherType()) && fee.getId() != null) {
                    feeMap.put(fee.getId(), accountDocumentDetail);
                }
            }
            adCreateRequestVO.setFeeMap(feeMap);
        } else if (AdConstants.SPECIAL_ACC_SUBJECT_CODE_BANK.equals(subjectCode)) { //特殊科目:1
            String companyCode = adCreateRequestVO.getCompanyCode(); //公司
            String accNo = adCreateRequestVO.getPayerAccNo(); //账号

            //为关联方交易
            if ("0".equals(adCreateRequestVO.getRelatedParty())) {
                //收款凭证
                if (AmountTypeEnum.COLLECT_AMOUNT.getVoucherType().equals(accountDocumentInfo.getVoucherType())) {
                    log.info("特殊科目为1且为收款凭证,公司编码取供应商对应公司编码,账号取收款账号...");
                    companyCode = adCreateRequestVO.getSupplierCompanyCode();
                    accNo = adCreateRequestVO.getPayeeAccNo();
                }
            }
            //关联方交易付款凭证和之前流程一样

            log.info("获取公司对应的银行科目 cf_ad_company_acc_subject.SUBJECT_CODE");
            String actualSubjectCode = adAccountRuleService.getSubjectCodeByCompanyCode(companyCode, accNo);
            if (StringUtils.isEmpty(actualSubjectCode)) {
                log.error("特殊科目1处理时,按公司没有找到对应的银行会计科目,companyCode={}, 支付账号={}", companyCode, accNo);
                throw new BizException(MessageFormat.format("公司[{0}]没找到对应的银行会计科目", companyCode));
            }
            log.info("本次生成摘要===2");
            remark = this.buildAbstract(adCreateRequestVO, null, abstrctRule);
            accountDocumentDetail = this.adAccountDocumentDetailService.populateDocumentDetail(actualSubjectCode, dcFlag, adCreateRequestVO, amount, entrySeq++, accountDocumentInfo, remark);
            accountDocumentDetails.add(accountDocumentDetail);
        } else if (AdConstants.SPECIAL_ACC_SUBJECT_CODE_TEMP.equals(subjectCode)) { //特殊科目:2
            //为验收单生成凭证使用
            String actualSubjectCode;
            List<AdCreateDetailAmountRequestVO> detailAmountRequestVOS = adCreateRequestVO.getDetailAmounts();
            if (CollectionUtils.isEmpty(detailAmountRequestVOS)) {
                log.info("凭证生成时,会计科目配置为2,但缺少金额明细数据");
                throw new BizException("凭证生成时,缺少金额明细数据");
            }

            for (AdCreateDetailAmountRequestVO amountRequestVO : detailAmountRequestVOS) {
                actualSubjectCode = adAccountRuleService.getSubjectCodeByFee(amountRequestVO.getFeeItemsCode(), null, amountRequestVO.getAssetFlag());
                if (StringUtils.isEmpty(actualSubjectCode)) {
                    log.error("特殊科目2处理时,按费用行生成分录时没有找到对应的会计科目,feeItemCode={},assetFlag={}", amountRequestVO.getFeeItemsCode(), amountRequestVO.getAssetFlag());
                    throw new BizException(MessageFormat.format("金额明细行[{0}]没找到对应的会计科目", amountRequestVO.getFeeItemsName()));
                }

                //构造摘要信息
                log.info("本次生成摘要===4");
                AdCreateFeeRequestVO tempFee = new AdCreateFeeRequestVO();
                BeanUtils.copyProperties(amountRequestVO, tempFee);
                remark = this.buildAbstract(adCreateRequestVO, tempFee, abstrctRule);
                accountDocumentDetail = this.adAccountDocumentDetailService.populateDocumentDetail(actualSubjectCode, dcFlag, adCreateRequestVO, amountRequestVO.getAmount(), entrySeq++, accountDocumentInfo, remark);
                accountDocumentDetail.setFee(tempFee);
                accountDocumentDetail.setFeeJson(JSON.toJSONString(amountRequestVO));
                accountDocumentDetails.add(accountDocumentDetail);
            }

        } else {
            AdCreateFeeRequestVO fee = null;
            if (AmountTypeEnum.DEPOSIT_LOAN_AMOUNT.getVoucherType().equals(accountDocumentInfo.getVoucherType())) {
                //借款核销单的押金类型 摘要特殊处理,取任一押金行的供应商,其他字段不使用
                List<AdCreateFeeRequestVO> fees = this.getFeesForVoucherType(adCreateRequestVO, accountDocumentInfo.getVoucherType());
                fee = new AdCreateFeeRequestVO();
                fee.setSupplierIdDetail(fees.get(0).getSupplierIdDetail());
                fee.setSupplierNameDetail(fees.get(0).getSupplierNameDetail());

            }
            //如果是关联交易,任意取一条
            if (adCreateRequestVO.isRelatedParty()) {
                List<AdCreateFeeRequestVO> fees = this.getFeesForVoucherType(adCreateRequestVO, accountDocumentInfo.getVoucherType());
                if (!CollectionUtils.isEmpty(fees)) {
                    fee = fees.get(0);
                }
            }
            log.info("本次生成摘要===3");
            remark = this.buildAbstract(adCreateRequestVO, fee, abstrctRule);
            accountDocumentDetail = this.adAccountDocumentDetailService.populateDocumentDetail(subjectCode, dcFlag, adCreateRequestVO, amount, entrySeq++, accountDocumentInfo, remark);
            //借款核销单的供应商取行上第一条
            if (!Objects.isNull(fee)) {
                accountDocumentDetail.setFee(fee);
                accountDocumentDetail.setFeeJson(JSON.toJSONString(fee));
            }
            accountDocumentDetails.add(accountDocumentDetail);
        }
        return accountDocumentDetails;
    }

        上面这个方法给人的第一感觉就是代码较多。首先,方法的主体框架是由多个 if else 对于subjectCode进行判断构成,显然用状态模式来重构这个方法是再合适不过了。其次,当一个方法的参数多于3个时最好用一个参数对象来封装这些入参。除上述两个主要的优化点外,如果想做的更好话,还是有不少地方可以优化,比如其中调用的一个方法——getSubjectCodeByFee。

actualSubjectCode = adAccountRuleService.getSubjectCodeByFee(fee.getFeeItemsCode(), fee.getDeptType(), fee.getAssetFlag());

        因为这个方法的入参全部来自于fee对象,所以完全可以将 getSubjectCodeByFee() 方法放入到fee类中,像下面这样,从而让贫血的Fee变成一个充血的Fee。 

public class Fee {
    private String feeItemsCode;
    private String deptType;
    private Boolean isAsset;

    ... 其他属性省略

    public String getSubjectCode() {
        Map<String, Object> param = new HashMap<>(2);
        param.put("feeItemCode", feeItemCode);
        if (isAsset()) {
            param.put("isAsset", isAsset);
        } else {
            param.put("deptType", deptType);
        }
        SpringUtil.getBean(XXDao.class).getSubjectCodeByFee(param);
    }
}

    当然也可以这样。

    public String getSubjectCode(XXDao dao) {
        Map<String, Object> param = new HashMap<>(2);
        param.put("feeItemCode", feeItemCode);
        if (isAsset()) {
            param.put("isAsset", isAsset);
        } else {
            param.put("deptType", deptType);
        }
        dao.getSubjectCodeByFee(param);
    }

return Result还是 throw Exception,这是个问题        

public class BillCommonLogic {
    ...

    public Result checkContractAmtOccupy(Long billId) {
        Bill bill = billService.getOneById(billId);
        if (bill == null) {
            return Result.FAILURE.setMessage(ErrorMsgConstants.OBJECT_NOT_EXIST);
        }
        if (BillConstants.CONTRACT_AMT_OCCUPY_FAILED.equals(bill.getContractAmtOccupySuccess())) {
            return Result.FAILURE.setMessage(ErrorMsgConstants.NOT_ALLOW);
        }
        return Result.ok();
    }

    public Bill checkBillStatus(Long billId, String taskId) {
        Bill bill = billService.getOneById(billId);
        if (bill == null) {
            throw new BizException("单据不存在!");
        }
        if (bill.getBillStatus().equals(BillStateConstants.NOT_SUBMIT)) {
            logger.error(Constants.TASK_IS_REVOCATION);
            throw new BizException(Constants.TASK_IS_REVOCATION);
        }
        return bill;
    }
    ...
}

重构之后

public class Bill {

    ...
    private String billStatus;
    private String contractAmtOccupySuccess;
    ...
    
    public void checkOccupationOfContractAmount() {
        BizAssert.isTrue(!BillConstants.CONTRACT_AMT_OCCUPY_FAILED.equals(contractAmtOccupySuccess)
                , "合同金额占用失败");
    }

    public void checkStatusForProcess() {
        BizAssert.isTrue(!billStatus.equals(BillStatusConstants.NOT_SUBMIT), "单据已被撤回");
    }


    ...
}

        即使对BillCommonLogic中这两个方法进行了重构,但这两个方法在很多位置都有调用(差不多有一百多处),在一定程度上来说这也是一种代码重复。

        如何简化这些调用,也就是最好只写一次Bill.checkStatusForProcess()便可替代的所有调用。是的,AOP是改动最小的方案。

先这样吧,等看到不爽的再来吐。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值