zoukankan      html  css  js  c++  java
  • 避免复制与粘贴

    《重构之美》之一

    logo_black 在开发过程中,当你发现代码可以Copy-paste时,就意味着代码出现了重复。这是一种典型的反模式。William J.Brown等在著作AntiPatterns-Refactoring Software,Architecture, and Projects in Crisis(即《反模式——危机中软件、架构和项目的重构》)中认为这种形式的复用让开发的代码行数量虚假地增加,但是不能像其他形式的复用一样降低成本。Copy-Paste代码的方式违背了DRY(即不要重复你自己)原则,使得多处地方出现了同样或者相似的代码。这是一种征兆,一旦在方法中或方法之间开始Copy-Paste操作,就意味着需要采用Extract Method重构手法。在提取方法之后,还可以根据情况利用Move Method重构手法,将其搬移到一个类中,然后在原来的调用处转为对该类方法的调用。或者利用Replace Method with Method Object,将这些职责封装为专有的类。 

    在我的编程生涯中,碰到类似Copy-Paste的情况简直不胜枚举。在一次项目中,我们对开源项目Jasper Report进行了扩展。我们加入了对新报表类型(CJT_REPORT)的支持。在ReportParameterAction类中,我们需要对报表对象ReportUnit判断报表类型。于是,我在ReportParameterAction类中定义了如下的私有方法: 

    private void setReportUnitTypeByFileResource(ReportUnit reportUnit) { 

           final String JS_FILE_TYPE = "jrxml"; 

           ResourceReference reference = reportUnit.getMainReport(); 

           if (reference.isLocal()) { 

               FileResource resource = (FileResource)reference.getLocalResource(); 

               String fileType = resource.getFileType(); 

               if (fileType.toLowerCase().equals(JS_FILE_TYPE)){ 

                  reportUnit.setReportType(ReportUnit.JS_REPORT); 

               } else { 

                  reportUnit.setReportType(ReportUnit.CJT_REPORT);         

                }         

            }     

     }

    该方法根据FileResource中的文件类型获得对应的报表类型。这一方法在ReportParameterAction类的createWrappers()方法中被调用。 

        protected Event createWrappers(RequestContext context) { 

           ReportUnit reportUnit = loadReportUnit(context); 

           setReportUnitTypeByFileResource(reportUnit); 

            InitialRequestContext(context, reportUnit);

           int reportType = reportUnit.getReportType(); 

           if (reportType == ReportUnit.CJT_REPORT) {//……} 

           //…… 

         }

    后来,我发现在EngineServiceImpl类中同样需要判断报表的类型。然而,setReportUnitTypeByFileResource()方法却被定义为ReportParameterAction的私有方法,无法被EngineServiceImpl对象调用。最简单的做法是采用复制的方式,将这段代码复制到EngineServiceImpl中。好了,如果此时我们不能忍住copy-paste的简便所带来的诱惑,或许就会陷入重复的泥沼了。Copy的动作绝对应该鸣起刺耳的警声。我们需要对这一做法保持足够的警惕。 

    通过分析setReportUnitTypeByFileResource()方法,我发现该方法需要操作的对象均与ReportUnit有关,而本身该方法的职责就是要获得ReportUnit的类型,并对其字段reportType进行设置,因此,完全可以将它搬移到ReportUnit中(Move Method重构,不是吗?)。由于ReportUnit已经增加了对reportType字段的getset访问方法,我发现它们与我要重构的方法实有异曲同工之处,因而决定将其搬移到getReportType()方法中: 

        public int getReportType() { 

           if (hasReportTypeBeenSet()) { 

               return reportType; 

           }     

           return  getReportTypeByFileResource();

        }   

         private int getReportTypeByFileResource() { 

           final String CJT_FILE_TYPE = "cjtxml"; 

           int reportType = ReportUnit.JS_REPORT; 

           ResourceReference reference = this.getMainReport(); 

           if (reference != null) { 

               if (reference.isLocal()) { 

                  FileResource resource = (FileResource) reference 

                         .getLocalResource(); 

                  if (resource != null) { 

                      String fileType = resource.getFileType(); 

                      if (fileType.toLowerCase().equals(CJT_FILE_TYPE)) { 

                         reportType = ReportUnit.CJT_REPORT; 

                      } 

                  } 

               } 

           }

            return reportType;

         } 

        private boolean hasReportTypeBeenSet(){ 

           if (reportType != ReportUnit.JS_REPORT 

    && reportType != ReportUnit.CJT_REPORT) { 

               return false; 

           } else { 

               return true; 

           } 

        }

    注意,在以上过程中,除了使用Move Method之外,我还运用了Rename Method以及Extract Method等重构手法。 

    现在,对报表类型的获取与调用就变得简单了,在ReportParameterActionEngineServiceImpl类中,也规避了重复代码的产生。例如,在createWrappers()方法中的调用被更改为:

        protected Event createWrappers(RequestContext context) { 

           ReportUnit reportUnit = loadReportUnit(context); 

           setReportUnitTypeByFileResource(reportUnit); 

            InitialRequestContext(context, reportUnit);

           int reportType = reportUnit.getReportType(); 

           if (reportType == ReportUnit.CJT_REPORT) {//……} 

           //…… 

         }

  • 相关阅读:
    MAC下cocos2dx环境搭建
    eclipse混淆打包出错
    eclipseme升级
    MyEclipse 10 中增加插件
    j2me图片处理大全
    关于svn使用
    NFS相关
    BMP文件格式图解
    UDA1341TS
    OpenOCD初始化脚本(uboot)
  • 原文地址:https://www.cnblogs.com/wayfarer/p/1893650.html
Copyright © 2011-2022 走看看