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