月下无限连?拒绝无休止switch!
拒绝无休止switch
一、前言
前天碰到个需求,其实很简单,就是Excel导入,Excel模板长下面这样:
按我平常的逻辑是这样做的:
- 用文件输入流读取Excel,根据Excel的版本生成不同的对象,比如XSSFWorkbook或是HSSFWorkbook
- new一个工作簿,读取内容
- 按行遍历,按cell单元格读取
- 读取到值后,根据业务逻辑进行处理,最后存入entity
这个需求按这个逻辑下来,循环取值的代码是这样的:
1 if (CollectionUtils.isNotEmpty(rowList)) { 2 List<DtTableCheck> data = Lists.newArrayList(); 3 Map<String, String> paramValueMap; 4 for (int i = 0; i < rowList.size(); i++) { 5 paramValueMap = Maps.newLinkedHashMap(); 6 DtTableCheck dtc = new DtTableCheck(); 7 for (Entry<String, String> entry : rowList.get(i).entrySet()) { 8 switch (entry.getKey().trim()) { 9 case "检查编号": 10 //一堆业务处理 11 case "数据库": 12 //一堆业务处理 13 case "表": 14 //一堆业务处理 15 case "限制条件": 16 //一堆业务处理 17 case "检查规则": 18 //一堆业务处理 19 case "参数1": 20 //一堆业务处理 21 case "参数2": 22 //一堆业务处理 23 case "参数3": 24 //一堆业务处理 25 case "参数4": 26 //一堆业务处理 27 } 28 } 29 data.add(dtc); 30 }
注:原先的代码过于丑陋,所以用了我司封装的方法,将Excel内容读取到一个list中,再循环读取,可以看到代码依然冗长
这样做有一个问题,如果Excel模板变动或是业务逻辑变动,会牵一发而动全身,后端代码都要改,而且这样的代码可维护性极差,典型的面向过程编程。
于是,趁着周末,借助策略模式与工厂模式的思想,赶紧重构了代码。
二、重构
代码中重复的操作是频繁的根据Excel单元格名称去switch不同的处理逻辑,那我们把它抽离出来,即
1 /** 2 * 解析Excel数据 3 * @Author Cone 4 * @Date 2019/12/7 12:39 5 */ 6 public interface dealExcel { 7 8 void deal(Map.Entry<String, String> entry, DtTableCheck dtc); 9 }
传入map中的一个要素,和需要操作的entity,具体的业务处理由不同的实现类去做。
接下来我们写一个工厂,用来返回不同的实现类:
1 /** 2 * @Author Cone 3 * @Date 2019/12/7 12:49 4 */ 5 public class dealFactory { 6 7 private static Map<String, dealExcel> dealMaps = Maps.newConcurrentMap(); 8 9 public static dealExcel create(String name) { 10 return dealMaps.get(name); 11 } 12 13 public static void register(String name, dealExcel de) { 14 dealMaps.put(name, de); 15 } 16 17 }
dealMaps用来保存字段名称(比如检查编号、数据库、表等)和对应的操作实现类,create()方法根据传入的字段名称返回对应的实现类,register()方法则将字段名称与实现类保存到dealMaps中供我们调用。
这样听起来好像没什么问题,但是我什么时候注册这个实现类到dealMaps中去呢?我们以一个实现类来举例:
1 /** 2 * 处理限制条件字段 3 * @Author Cone 4 * @Date 2019/12/7 13:16 5 */ 6 @Service 7 public class dealQueryCondition implements dealExcel, InitializingBean { 8 @Override 9 public void deal(Map.Entry<String, String> entry, DtTableCheck dtc) { 10 dtc.setQueryCondition(null == entry.getValue() ? null : entry.getValue().trim()); 11 } 12 13 @Override 14 public void afterPropertiesSet() throws Exception { 15 dealFactory.register("限制条件", this); 16 } 17 }
这个实现类用来处理 Excel中 “限制条件”这一字段,我们在deal()方法中去完成具体的处理逻辑。接下来重点来了,可以看到,这个类还实现了一个接口,即InitializingBean,它是Spring提供的,这个接口里面有一个方法afterPropertiesSet(),用来做属性初始化后的相关操作,凡是继承该接口的类,在bean的属性初始化后,都会执行该方法,我们这里将实现类注册进去。
接下来就很简单了,只需要根据业务去完成实现类即可。这样改造完后,程序的调用是这样的:
1 List<Map<String, String>> rowList = ExcelHelper.readExcelSheet(file.getPath()); 2 if (CollectionUtils.isNotEmpty(rowList)) { 3 4 for (int i = 0; i < rowList.size(); i++) { 5 DtTableCheck dtc = new DtTableCheck(); 6 for (Entry<String, String> entry : rowList.get(i).entrySet()) { 7 dealExcel de = dealFactory.create(entry.getKey()); 8 de.deal(entry, dtc); 9 } 10 11 } 12 13 }
rowList即为Excel中的数据,数据按行存入list,每一行的数据被放入map中,类似这样:
1 [ 2 "检查编号":value, 3 "数据库":value, 4 "限制条件":value 5 ]
改造后的效果不用我多说了。
三、结语
我之所以要改造原有代码是因为这个需求在不断变化,模板也在调整,但是如果Excel本来就2,3个字段,或者业务变动不大,那我觉得就没有必要做改造了,改造成本,开发效率需要自己去权衡。运用设计模式应该让代码更好维护,而不是更糟,对吧。