1. 概述

代码重构是一个很大的话题,比如单一职责原则,DRY(don’t repeat yourself),KISS(keep it simple and stupid) ,开闭原则等等。
下面我们在写代码时一些案例来说明我们需要注意的地方。

2 案例

2.1 不要长方法

public List<Map<String,Object>> readExcelValue(Workbook wb,List<FormBoAttr> headers,InputStream is,boolean isXls){
        List<Map<String, Object>> dataList = new ArrayList<>();
        // 得到第一个shell
        Sheet sheet = wb.getSheetAt(0);
        // 得到Excel的行数
        int totalRows = sheet.getPhysicalNumberOfRows();
        // 得到Excel的列数(前提是有行数)
        int totalCells = 0;
        if (totalRows > 1 && sheet.getRow(0) != null) {
            totalCells = sheet.getRow(1).getPhysicalNumberOfCells();
        }
        Map<Integer,FormBoAttr> titleMap = new HashMap<>(totalCells);

        //只读取数据行,标题行跟列头行忽略掉
        for (int r = 2; r < totalRows; r++) {
            Row row = sheet.getRow(r);
            if (row == null){
                continue;
            }
            Map<String,Object> map = new HashMap<>(totalCells);
            // 循环Excel的列
            for (int c = 0; c < totalCells; c++) {
                Cell cell = row.getCell(c);
                if (null != cell) {

                    //如果是纯数字,比如你写的是25,cell.getNumericCellValue()获得是25.0,通过截取字符串去掉.0获得25
                    FormBoAttr title = headers.get(c);
                    if(title!=null){
                        if("string".equals(title.getDataType())) {
                            cell.setCellType(CellType.STRING);
                        }
                        String value=ExcelUtil.getValueByCellType(cell);
                        String titleName = title.getFieldName();
                        if("date".equals(title.getDataType())){
                            String formStr = StringUtils.isNotEmpty(title.getFormat()) ? title.getFormat().trim() : DateUtils.getCnDateStr(value);
                            Date d = DateUtils.parseDate(value, formStr);
                            map.put(titleName, d);
                        }else {
                            map.put(titleName, value);
                        }
                    }
                }
            }
            // 添加到list
            dataList.add(map);
        }
        return dataList;
    }

在这个代码的问题:

  1. 写for循环 ,索引变量 最好使用,i,m,n,j等变量。

  2. 在写判断时需要早点退出循环。

  3. 抽离私有方法

重构后的代码

public List<Map<String,Object>> readExcelValue(Workbook wb,List<FormBoAttr> headers,InputStream is,boolean isXls){
        List<Map<String, Object>> dataList = new ArrayList<>();
        // 得到第一个shell
        Sheet sheet = wb.getSheetAt(0);
        // 得到Excel的行数
        int totalRows = sheet.getPhysicalNumberOfRows();
        // 得到Excel的列数(前提是有行数)
        int totalCells = 0;
        if (totalRows > 1 && sheet.getRow(0) != null) {
            totalCells = sheet.getRow(1).getPhysicalNumberOfCells();
        }

        //只读取数据行,标题行跟列头行忽略掉
        for (int r = 2; r < totalRows; r++) {
            Row row = sheet.getRow(r);
            if (row == null){
                continue;
            }
            Map<String, Object> map = handRow(headers, totalCells, row);
            // 添加到list
            dataList.add(map);
        }
        return dataList;
    }

    private Map<String, Object> handRow(List<FormBoAttr> headers, int totalCells, Row row) {
        Map<String,Object> map = new HashMap<>(totalCells);
        // 循环Excel的列
        for (int i = 0; i < totalCells; i++) {
            Cell cell = row.getCell(i);
            if(cell==null){
                continue;
            }
            //如果是纯数字,比如你写的是25,cell.getNumericCellValue()获得是25.0,通过截取字符串去掉.0获得25
            FormBoAttr attr = headers.get(i);
            if(attr==null){
                continue;
            }

            if("string".equals(attr.getDataType())) {
                cell.setCellType(CellType.STRING);
            }
            String value= ExcelUtil.getValueByCellType(cell);
            String field = attr.getFieldName();
            if("date".equals(attr.getDataType())){
                String formStr = StringUtils.isNotEmpty(attr.getFormat()) ? attr.getFormat().trim() : DateUtils.getCnDateStr(value);
                Date date = DateUtils.parseDate(value, formStr);
                map.put(field, date);
            }else {
                map.put(field, value);
            }

        }
        return map;
    }

上例将一行数据单独抽离,这样代码可读性更好,逻辑更清晰。

2.2 及时返回原则

有些写程序的时候记得早点返回,减少代码缩进层次。

public List getTaskId(List data) {
        JSONArray instIds=new JSONArray();
        for (int i = 0; i < data.size(); i++) {
            Map<String,Object> map = (Map<String, Object>) data.get(i);
            Object instId = map.get("INST_ID_");
            if(BeanUtil.isNotEmpty(instId)){
                instIds.add(instId.toString());
            }
        }
        if(instIds.size()>0){
            List<Map<String, String>> taskIds = bpmInstClient.getTaskIds(instIds.toJSONString());
            if(BeanUtil.isNotEmpty(taskIds) && taskIds.size()>0 ){
                for (int i = 0; i < taskIds.size(); i++) {
                    Map<String, String> taskIdMap = taskIds.get(i);
                    for (int j = 0; j < data.size(); j++) {
                        Map<String,Object> dataMap = (Map<String, Object>) data.get(j);
                        if(BeanUtil.isNotEmpty(dataMap.get("INST_ID_")) && taskIdMap.get("instId").equals(dataMap.get("INST_ID_").toString())){
                            dataMap.put("taskId",taskIdMap.get("taskId"));
                        }
                    }
                }
            }
        }
        return data;
    }

像这个代码的缩进有些多。

重构后的代码

public List getTaskId(List data) {
        JSONArray instIds=new JSONArray();
        for (int i = 0; i < data.size(); i++) {
            Map<String,Object> map = (Map<String, Object>) data.get(i);
            Object instId = map.get("INST_ID_");
            if(BeanUtil.isNotEmpty(instId)){
                instIds.add(instId.toString());
            }
        }

        if(instIds.size()==0){
            return data;
        }

        List<Map<String, String>> taskIds = bpmInstClient.getTaskIds(instIds.toJSONString());

        if(taskIds.size()==0){
            return data;
        }

        for (int i = 0; i < taskIds.size(); i++) {
            Map<String, String> taskIdMap = taskIds.get(i);
            for (int j = 0; j < data.size(); j++) {
                Map<String,Object> dataMap = (Map<String, Object>) data.get(j);
                if(BeanUtil.isNotEmpty(dataMap.get("INST_ID_")) && taskIdMap.get("instId").equals(dataMap.get("INST_ID_").toString())){
                    dataMap.put("taskId",taskIdMap.get("taskId"));
                }
            }
        }

        return data;
    }

在这里做了判断当数据为0的时候就及时返回了,这样代码的缩进就少了两层。逻辑也会更清楚。

2.3 单一职责原则

有时候我们在写一个大而全的类,导致整个类中什么都有,其实可以将一些功能抽离,比如列表的权限计算。

我们将权限计算的这个类从 FormBoListServiceImpl 中抽离了一下,将权限计算抽到 PermissionSqlService 这个代码中。发现这个权限计算的类 有 564行代码。

抽离的好处在于,逻辑更加清楚,维护权限的计算逻辑更集中,封装性更好,对外只有两个公共的方法。对代码的维护也更方便一些,我们对权限的计算,只需要维护这个类就好,当然还可以继续重构。做到符合开闭原则。

文档更新时间: 2021-05-21 10:03   作者:zyg