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;
}
在这个代码的问题:
写for循环 ,索引变量 最好使用,i,m,n,j等变量。
在写判断时需要早点退出循环。
抽离私有方法
重构后的代码
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