拒绝无休止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个字段,或者业务变动不大,那我觉得就没有必要做改造了,改造成本,开发效率需要自己去权衡。运用设计模式应该让代码更好维护,而不是更糟,对吧。

版权声明:本文为cone原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/cone/p/12002913.html