消除“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的意图,这意味着,当由别的同事接手或者查看这部分代码时,会显得不知所措,彻底不理解为何这段代码要这样设计,这须要沟通到位。

相关文章
相关标签/搜索