月下無限連?拒絕無休止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個字段,或者業務變動不大,那我覺得就沒有必要做改造了,改造成本,開發效率需要自己去權衡。運用設計模式應該讓代碼更好維護,而不是更糟,對吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章