消除「if...else」實戰

        相信很多朋友在開發的時候都會碰到過這種問題:大量使用if...else進行邏輯判斷。不論是接手過來的系統,仍是本身開發的系統,當你看到一大堆的if...else語句以後,內心總會冒出一句「這簡直就是一堆shit」。最近博主熱衷於重構代碼,看到本身手上的系統的一些判斷邏輯,巴不得將它所有刪掉,再加上最近也接觸到很多關於消除if...else的方法介紹文章,不禁得本身也來搞搞。html

        言歸正傳,概括一下博主瀏覽過的一些關於消除if...else的方法,基本上最高效的手段就是:設計模式+反射。怎麼講?設計模式有一個很明顯的好處,就是使得類之間解耦,易於擴展,在後面業務變化的時候,更方便開發與維護。不少文章都說起到使用策略模式,不過此次博主重構了一下手上的一個系統的一個業務功能的代碼,用到了工廠方法模式+模板模式+反射。編程

(關於設計模式能夠點擊:設計模式

一、策略模式:https://www.cnblogs.com/SysoCjs/p/10395457.htmlapp

二、工廠方法模式:https://www.cnblogs.com/SysoCjs/p/10327165.htmlide

三、模板模式:https://www.cnblogs.com/SysoCjs/p/10327088.html)spa

        首先,介紹一下所重構的功能業務需求。在UI上,根據所選擇的Excel模板類型進行下載;根據用戶上傳的不一樣的Excel模板進行數據上傳。這是一個很簡單的功能,就是上傳和下載。UI效果以下(作得比較醜):
             設計

        當用戶選擇模板類型(「Template Type」),填寫相關信息後,點擊下載(「Template Download」),在後臺會進行一些填寫信息判斷,假設全部數據都沒有問題了,就真正進入咱們的Excel模板下載邏輯(因爲博主擅長於Java,因此在C#規範方面參雜了很多Java的編程規範,請讀者不要介意)。excel

初版邏輯:code

public void DownloadTemplateFile( string templateFile,HttpResponse response, List<DataTable> dtList, DownloadTemplateInputClass dtic) { . . . //判斷模板類型 if (TemplateFile.Master.ToString().Equals(templateFile.Trim())) { ... } else if (TemplateFile.Calendar.ToString().Equals(templateFile.Trim())) { ... } else if (TemplateFile.CapacityRatio.ToString().Equals(templateFile.Trim())) { ... } else if(TemplateFile.Vendor.ToString().Equals(templateFile.Trim())) { ... } else if(TemplateFile.ResGpCapacity.ToString().Equals(templateFile.Trim())) { ... } if (createType.Equals("homemade"))//狀態,用於識別是自制模板,仍是下載已有模板)  { ... } else//非自制模板,使用原來邏輯  { int startIndex = filePath.LastIndexOf("\\") + 1;//使用了轉義符,獲得文件名的index位置 fileName = filePath.Substring(startIndex);//獲得文件名+後綴  FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read); byte[] bytes = new byte[fs.Length]; fs.Read(bytes, 0, bytes.Length);//讀取文件,從fs指定的文件中讀取0~bytes.Length字節的內容,寫入到bytes中  response.Clear(); response.AddHeader("Content-Length", fs.Length.ToString()); response.ContentType = "application/x-xls"; response.AddHeader("Content-Disposition", "attachment;FileName=" + fileName); fs.Close(); response.BinaryWrite(bytes); } response.OutputStream.Flush(); response.OutputStream.Close(); }

        嗯~看起來比較抓狂,一個類文件處理了全部模板文件Excel的生成,若是業務擴展,須要增長新類型模板,又要往這個類文件塞東西,想一想就以爲噁心。因而便出現了工廠方法模式的使用,建立分別一個抽象工廠類,抽象模板產品類,實體模板工廠類,每一個類型模板歸屬一個實體模板產品類,那麼類文件以下:
htm

UML圖:

AbstractExcelRobotFactory抽象類:

public abstract class AbstractExcelRobotFactory { protected enum TemplateFile { Vendor = 0, Master = 1, StepMapping = 2, Calendar = 3, CapacityRatio = 4, ResGpCapacity = 5, VendorCapacity = 6 } protected string titleStr; protected string templateFile; protected TipsMsgLanguage tipsMsgLanguage; protected AbstractTemplateProduct abstractTemplateProduct; /// <summary> /// 生成模板對象實例,用於將上傳的excel文檔數據轉化成DataTable數據,顯示在頁面上 /// </summary> /// <returns></returns> public abstract AbstractTemplateProduct createTemplateInstanceForUpload(); /// <summary> /// 生成模板對象實例,用於生成excel模板,供users下載 /// </summary> /// <returns></returns> public abstract AbstractTemplateProduct createTemplateInstanceForDownload(); }

AbstractTemplateProduct抽象類:

public abstract class AbstractTemplateProduct { protected TipsMsgLanguage tipsMsgLanguage; /// <summary> /// 操做數據,生成DataTable /// </summary> public abstract void operateTemplateDate(IWorkbook workBook, DataTable uploadTable); public abstract void downloadTemplate(HttpResponse response, List<DataTable> dtList, DownloadTemplateInputClass dtic); }

上面的DownloadTemplateFile方法,改進後的代碼:

public void DownloadTemplateFile( string templateFile,HttpResponse response, List<DataTable> dtList, DownloadTemplateInputClass dtic) { AbstractExcelRobotFactory abstractExcelRobotFactory = new ExcelRobotFactory(templateFile, TipsMsgLanguage); AbstractTemplateProduct abstractTemplateProduct = abstractExcelRobotFactory.createTemplateInstanceForDownload(); try { abstractTemplateProduct.downloadTemplate(response, dtList, dtic); } catch (Exception e) { ... } }

        DownloadTemplateFile()方法做用:首先使用傳進來的方法實例化一個實體工廠類,這個實例化的操做,有兩個功能:一、建立ExcelRobotFactory對象;二、存儲templateFile等數據(這個很重要);當有了實體工廠類對象以後,就能夠建立模板類對象了;有了模板類對象,就能夠調用相應的方法,實現業務功能。

下面看一下ExcelRobotFactory的建立模板實例的createTemplateInstanceForDownload()方法代碼:

沒有使用反射(初版):

public override AbstractTemplateProduct createTemplateInstanceForDownload() { //判斷模板類型 if (TemplateFile.Master.ToString().Equals(templateFile.Trim())) { abstractTemplateProduct = new MasterTemplateExcel(fileName, tipsMsgLanguage); } else if (TemplateFile.ResGpCapacity.ToString().Equals(templateFile.Trim())) { abstractTemplateProduct = new ResGpCapTemplateExcel(fileName, tipsMsgLanguage); }else if (TemplateFile.Vendor.ToString().Equals(templateFile.Trim())) { abstractTemplateProduct = new VendorTemplateExcel(fileName, tipsMsgLanguage); } else if (TemplateFile.VendorCapacity.ToString().Equals(templateFile.Trim())) { abstractTemplateProduct = new VendorCapTemplateExcel(fileName, tipsMsgLanguage); } else { abstractTemplateProduct = new OtherTemplateExcel(fileName, filePath, tipsMsgLanguage); } return abstractTemplateProduct; }

使用了反射(第二版):

public override AbstractTemplateProduct createTemplateInstanceForDownload() { ... //設置參數parameters,用於建立帶參的類對象 Object[] parameters = {filePath, tipsMsgLanguage }; // 獲取當前程序集 Assembly assembly = Assembly.GetExecutingAssembly(); try {//根據全類名,經過反射建立類對象。這裏用到的參數只有DictionaryUtil.templateDictionary[templateFile.ToString()]和parameters,其餘是默認的 abstractTemplateProduct = (AbstractTemplateProduct)assembly.CreateInstance(DictionaryUtil.templateDictionary[templateFile.ToString()], true, BindingFlags.Default, null, parameters, null, null); } catch (Exception e) { //Console.WriteLine(e);  } return abstractTemplateProduct; }

        沒有使用反射的時候,仍是須要使用大量的if...else來判斷到底須要建立哪種具體模板類,可是使用了反射以後,直接兩句代碼就搞掂了,並且沒有任何if...else語句,這裏不得不說一個關鍵性參數DictionaryUtil.templateDictionary[templateFile.ToString()],DictionaryUtil是一個類,裏面定義了一個字典類型的數據:

public class DictionaryUtil { protected enum TemplateFile { Vendor = 0, Master = 1, StepMapping = 2, Calendar = 3, CapacityRatio = 4, ResGpCapacity = 5, VendorCapacity = 6 } public static Dictionary<string, string> templateDictionary = new Dictionary<string, string> { {TemplateFile.Master.ToString(),"CapacityManagementSystem.Logic.MasterTemplateExcel"}, {TemplateFile.ResGpCapacity.ToString(),"CapacityManagementSystem.Logic.ResGpCapTemplateExcel"}, {TemplateFile.Vendor.ToString(),"CapacityManagementSystem.Logic.VendorTemplateExcel"}, {TemplateFile.VendorCapacity.ToString(),"CapacityManagementSystem.Logic.VendorCapTemplateExcel"}, {TemplateFile.Calendar.ToString(),"CapacityManagementSystem.Logic.OtherTemplateExcel"}, {TemplateFile.CapacityRatio.ToString(),"CapacityManagementSystem.Logic.OtherTemplateExcel"} }; }

這是根據以前實例化實體工廠類對象時保存的templateFile數據,來獲取對應全類名字符串,再根據全類名字符串,經過反射建立類對象,這就完徹底全拋棄了if...else語句的使用。固然,也能夠直接將這個字典類型數據放在ExcelRobotFactory類文件裏面,考慮到後面維護與開發,博主選擇將它做爲一個獨立的類使用,就相似於配置文件。

        總結:本次重構使用了工廠方法模式+模板模式+反射,在服務層使用工廠方法模式,去掉if...else判斷,在建立實體模板類時,使用反射機制。沒有重構以前,代碼臃腫,維護麻煩,新增需求時,須要從新查看邏輯,而後添加相應邏輯判斷,以及邏輯實現,一不當心就會漏掉一些邏輯判斷沒有添加或修改;重構以後,維護方便,容易定位問題所在歸屬哪個類文件,當須要增長或減小模板類型時,只須要增長或加少相應的模板類,以及增長或刪除字典類型數據的元素。但不得不注意一個問題,使用設計模式,是取決於developer的意圖,這意味着,當由別的同事接手或者查看這部分代碼時,會顯得不知所措,徹底不理解爲何這段代碼要這樣設計,這須要溝通到位。

相關文章
相關標籤/搜索