1. 首页 > 电脑教程 > 《重构之美》之避免复制与粘贴

《重构之美》之避免复制与粘贴

在开发过程中,当你发现代码可以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字段的get和set访问方法,我发现它们与我要重构的方法实有异曲同工之处,因而决定将其搬移到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等重构手法。 现在,对报表类型的获取与调用就变得简单了,在ReportParameterAction与EngineServiceImpl类中,也规避了重复代码的产生。例如,在createWrappers()方法中的调用被更改为: protected Event createWrappers(RequestContext context) { ReportUnit reportUnit = loadReportUnit(context);

setReportUnitTypeByFileResource(reportUnit);

InitialRequestContext(context, reportUnit); int reportType = reportUnit.getReportType(); if (reportType == ReportUnit.CJT_REPORT) {//……} //…… }

声明:希维路由器教程网提供的内容,仅供网友学习交流,如有侵权请与我们联系删除,谢谢。ihuangque@qq.com
本文地址:https://www.ctrlcv.com.cn/diannao/169323122610585.html