重构:提炼方法

提炼方法顾名思义是将一个代码片段提炼出来放在一个独立的方法中。提炼方法是重构手段中最简单也是最常用的一个手段,我自己在重构一段代码中,很多时候第一件事就是提炼方法。

那么该怎么使用这个手段呢,什么情况下需要使用这个手段。很多人可能也都知道,当一个方法较长时,就可以提炼方法来保证每个方法都足够的精短;当一个代码片段被多个地方使用时,考虑到代码复用,我们就可以将其提炼出来。这两种情况都没有错,但我认为最合理的一个观点是:提炼方法,以将意图与实现分开。如果你需要花时间浏览一段代码才弄清它到底在干什么,那么就应该将其提炼到一个方法中,并根据它所做的事为其命名。以后再读到这段代码时,我们通过方法名就能知道方法的用途,大多时候并不关系方法是怎么达到目的的。

方法名应该描述方法的用途,而不是方法是怎么实现的。

我们先看下下面这段代码:

public static void merge(BInvoiceReg ivcReg) {
    List<BIvcRegSumLine> bIvcRegSumLines = new ArrayList<BIvcRegSumLine>();
    int lineNumber = 0;
    // 按照合同,科目,税率,单价进行分组
    Collection<List> group = CollectionHelper.group(ivcReg.getRegLines(),
                                                    new Comparator<IvcRegLine>() {
                                                        @Override
                                                        public int compare(IvcRegLine o1, IvcRegLine o2) {
                                                            if (ObjectUtils.equals(o1.getAcc1().getContract().getUuid(), o2.getAcc1().getContract()
                                                                                   .getUuid())
                                                                && ObjectUtils.equals(o1.getAcc1().getSubject().getUuid(), o2.getAcc1()
                                                                                      .getSubject().getUuid())
                                                                && ObjectUtils.equals(o1.getAcc1().getTaxRate().getRate(), o2.getAcc1()
                                                                                      .getTaxRate().getRate()) && ObjectUtils.equals(o1.getPrice(), o2.getPrice())){
                                                                return 0;
                                                            } else{
                                                                return 1;
                                                            }
                                                        }
                                                    });
    for (List<IvcRegLine> regLines : group) {
        if (regLines.size() > 1) {
            // 按照起始日期升序
            Collections.sort(regLines, new Comparator<IvcRegLine>() {
                @Override
                public int compare(IvcRegLine o1, IvcRegLine o2) {
                    if (o1.getAcc1().getBeginTime() == null) {
                        return -1;
                    } else if (o2.getAcc1().getBeginTime() == null) {
                        return 1;
                    } else {
                        return o1.getAcc1().getBeginTime().compareTo(o2.getAcc1().getBeginTime());
                    }
                }
            });
            // 根据账期二次分组的结果(用于校验)
            List<List<IvcRegLine>> lists = new ArrayList<List<IvcRegLine>>();
            Map<DateRange,List<IvcRegLine>> rangeListMap = new HashMap<>();
            for (int i = 0; i < regLines.size(); i++) {
                boolean isContain = false;
                List<IvcRegLine> ivcRegLines = new ArrayList<IvcRegLine>();
                isContain = isContain(lists, regLines.get(i));
                if (!isContain) {
                    ivcRegLines.add(regLines.get(i));
                    Date tmpBegin = regLines.get(i).getAcc1().getBeginTime() == null ? null
                        : (Date) regLines.get(i).getAcc1().getBeginTime().clone();
                    Date tmpEnd = regLines.get(i).getAcc1().getEndTime() == null ? null : (Date) regLines
                        .get(i).getAcc1().getEndTime().clone();
                    DateRange dateRange = new DateRange(tmpBegin,tmpEnd);
                    rangeListMap.put(dateRange,ivcRegLines);
                    lists.add(ivcRegLines);
                    mergeIncludeDateRange(i,dateRange,lists,regLines,ivcRegLines);
                }
                if (!CollectionUtils.isEmpty(ivcRegLines)) {
                    lineNumber = lineNumber + 1;
                    bIvcRegSumLines.add(createNewSumLine(ivcRegLines, lineNumber));
                }
            }
        } else {
            lineNumber = lineNumber + 1;
            bIvcRegSumLines.add(createNewSumLine(regLines, lineNumber));
        }
    }
    ivcReg.setIvcRegSumLines(bIvcRegSumLines);
}

如果让你们去维护这段代码,你们一定跟我一样。

(1)一个方法写这么长,有毛病吧。

(2)这都写了些啥,它想干嘛,谁能告诉我。

(3)改不动,还是不改了,重新写一个吧。

上面的代码是我工作项目中真是存在的一段代码,当时碰到一个死循环的问题,然后被迫来修复这段代码。实际上这段代码的逻辑并不复杂,但刚拿到这段代码,光理解逻辑就理了一天(主要逻辑在mergeIncludeDateRange方法,这里就不展示了)。不过最终我也没在这个代码上改,而是……另外写了。如下:

public static List<BIvcRegSumLine> merge(List<IvcRegLine> regLines) {
    List<BIvcRegSumLine> result = new ArrayList<>();
    if (CollectionUtils.isEmpty(regLines)) {
        return result;
    }

    Collection<List> lists = groupByLineKey(regLines);

    for (List<IvcRegLine> lines : lists) {
        List<BIvcRegSumLine> sumLines = createSumLines(lines);
        if (sumLines.isEmpty() == false) {
            result.addAll(sumLines);
        }
    }

    sortBySubjectCodeASC(result);

    LineNumberGenerator lineNumberGenerator = new LineNumberGenerator();
    result.forEach(line -> line.setLineNumber(lineNumberGenerator.next()));

    return result;
}

看完重构后的这段方法,也许不能完全分析出整个方法的逻辑,但至少大意能够完全理清楚吧?

(1)将入参regLines按照lineKey进行分组,其中lineKey是用于表示一个InvRegLine对象的复合键。

(2)循环遍历每组,然后根据组内的lines创建sumLines,这里其实是根据一定规则将lines再次分组并汇总数据。

(3)将汇总后的数据按照subjectCode字段升序排序。

所以你们是喜欢看重构前还是重构后的代码呢?

——End——
更多精彩分享,可扫码关注微信公众号哦。

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值