zoukankan      html  css  js  c++  java
  • 消除“if...else”实战

            相信不少朋友在开发的时候都会碰到过这种问题:大量使用if...else进行逻辑判断。不管是接手过来的系统,还是自己开发的系统,当你看到一大堆的if...else语句之后,心里总会冒出一句“这简直就是一堆shit”。最近博主热衷于重构代码,看到自己手上的系统的一些判断逻辑,恨不得将它全部删掉,再加上最近也接触到不少关于消除if...else的方法介绍文章,不由得自己也来搞搞。

            言归正传,归纳一下博主浏览过的一些关于消除if...else的方法,基本上最高效的手段就是:设计模式+反射。怎么讲?设计模式有一个很明显的好处,就是使得类之间解耦,易于扩展,在后面业务变化的时候,更方便开发与维护。很多文章都提及到使用策略模式,不过这次博主重构了一下手上的一个系统的一个业务功能的代码,用到了工厂方法模式+模板模式+反射。

    (关于设计模式可以点击:

    1、策略模式:https://www.cnblogs.com/SysoCjs/p/10395457.html

    2、工厂方法模式:https://www.cnblogs.com/SysoCjs/p/10327165.html

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

            首先,介绍一下所重构的功能业务需求。在UI上,根据所选择的Excel模板类型进行下载;根据用户上传的不同的Excel模板进行数据上传。这是一个很简单的功能,就是上传和下载。UI效果如下(做得比较丑):
                 

            当用户选择模板类型(“Template Type”),填写相关信息后,点击下载(“Template Download”),在后台会进行一些填写信息判断,假设所有数据都没有问题了,就真正进入我们的Excel模板下载逻辑(由于博主擅长于Java,所以在C#规范方面参杂了不少Java的编程规范,请读者不要介意)。

    第一版逻辑:

    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的生成,如果业务扩展,需要增加新类型模板,又要往这个类文件塞东西,想想就觉得恶心。于是便出现了工厂方法模式的使用,创建分别一个抽象工厂类,抽象模板产品类,实体模板工厂类,每个类型模板归属一个实体模板产品类,那么类文件如下:

    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()方法作用:首先使用传进来的方法实例化一个实体工厂类,这个实例化的操作,有两个功能:1、创建ExcelRobotFactory对象;2、存储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的意图,这意味着,当由别的同事接手或者查看这部分代码时,会显得不知所措,完全不理解为什么这段代码要这样设计,这需要沟通到位。

  • 相关阅读:
    博客园美化-SimpleMemor
    Java多线程-synchronized与ReentrantLock
    springboot中删除@SessionAttributes注解的属性
    SSM整合笔记
    Spring中xml和注解方式使用AOP
    Mysql 数据库基本操作
    Mysql 二进制包安装
    named piped tcp proxy 下载
    docker容器中日志文件过大处理方法
    自动做bond的脚本
  • 原文地址:https://www.cnblogs.com/SysoCjs/p/10534869.html
Copyright © 2011-2022 走看看