西西软件园多重安全检测下载网站、值得信赖的软件下载站!
软件
软件
文章
搜索

首页编程开发其它知识 → 《重构之美》之避免复制与粘贴

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

相关软件相关文章发表评论 来源:本站整理时间:2010/12/1 18:52:01字体大小:A-A+

作者:佚名点击:33次评论:0次标签: 重构之美 复制 粘贴

DVDFab PlatinumV9.2.0.1 Final 多国语言官方安装版
  • 类型:光盘工具大小:74.4M语言:中文 评分:6.0
  • 标签:
立即下载
在开发过程中,当你发现代码可以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) {//……}

//……

}

    相关评论

    阅读本文后您有什么感想? 已有人给出评价!

    • 8 喜欢喜欢
    • 3 顶
    • 1 难过难过
    • 5 囧
    • 3 围观围观
    • 2 无聊无聊

    热门评论

    最新评论

    发表评论 查看所有评论(0)

    昵称:
    表情: 高兴 可 汗 我不要 害羞 好 下下下 送花 屎 亲亲
    字数: 0/500 (您的评论需要经过审核才能显示)