求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
 
重构实战案例一,以子类取代类型编码
 

2010-09-16 作者:Nicholas 来源:obpm的blog

 

1. 何谓重构

1.1名词解释

对软件内部结构的一种调整,目的是在不改变「软件之可察行为」前提下,提高其可理解性,降低其修改成本。

1.2动词解释

使用一系列重构准则(手法),在不改变「软件之可察行为」前提下,调整其结构。

2. 为何重构

2.1「重构」改进软件设计

同样完成一件事,设计不良的程序往往需要更多代码,这常常是因为代码在不同的地方使用完全相同的语句做同样的事。因此改进设计的一个重要方向就是消除重复代码(Duplicate Code)

2.2「重构」使软件更易被理解

你的源码还有其它读者:数个月之后可能会有另一位程序员尝试读懂你的代码并做一些修改。我们很容易忘记这第二位读者,但他才是最重要的。计算器是否多花了数个钟头进行编译,又有什么关系呢?如果一个程序员花费一周时间来修改某段代码,那才关系重大— 如果他理解你的代码,这个修改原本只需一小时

2.3「重构」助你找到臭虫 ( bugs)

Kent Beck 经常形容自己的一句话:『我不是个伟大的程序员;我只是个有着一些优秀习惯的好程序员而已。』重构能够帮助我更有效地写出强固稳健(robust)的代码。

2.4「重构」助你提高编程速度

终于,前面的一切都归结到了这最后一点:重构帮助你更快速地开发程序。听起来有违反直觉。当我谈到重构,人们很容易看出它能够提高质量。改善设计、提升可读性、减少错误,这些都是提高质量。但这难道不会降低开发速度吗?我强烈相信:良好设计是快速软件开发的根本。事实上拥有良好设计才可能达成快速的开发。如果没有良好设计,或许某一段时间内你的进展迅速,但恶劣的设计很快就让你的速度慢下来。你会把时间花在调试上面,无法添加新功能。修改时间愈来愈长,因为你必须花愈来愈多的时间去理解系统、寻找重复代码。随着你给最初程序打上一个又一个的补丁(patch),新特性需要更多代码才能实现。真是个恶性循环。

3.何时重构?

重构本来就不是一件「特别拨出时间做」的事情,重构应该随时随地进行。你不应该为重构而重构,你之所以重构,是因为你想做别的什么事,而重构可以帮助你把那些事做好

3.1三次法则(The Rule of Three)

Don Roberts 给了我一条准则:第一次做某件事时只管去做;第二次做类似的事会产生反感,但无论如何还是做了;第三次再做类似的事,你就应该重构。

☆ 事不过三,三则重构。(Three strikes and you refactor.)

3.2重构时机

添加功能时一并重构

修补错误时一并重构

复审代码时一并重构

-以上章节摘抄自《重构-改善既有代码的设计》

4.平台重构案例

4.1重构动机

1. 实例模块为View,重构元素为ViewAction、ViewProcessBean、View,以下为关系图。

2. 由于View存在多种editMode(编辑模式),而每个调用的地方都需要进行type code(类型码)判断,然后再进行相应的业务逻辑处理,最终在每个调用的地方都形成了大量的if-else代码,大大减弱了代码的可读性,和逻辑清晰度。

3. 调用的地方:

4.2重构作法

如上图所示,将每种type code重构成subclass,加强了每种类型处理业务逻辑的能力。

View-版本1566代码片段:

public EditMode getEditModeType() {
if (EDIT_MODE_CODE_DQL.equals(getEditMode())) {
return new DQLEditMode(this);
}
else if (EDIT_MODE_CODE_SQL.equals(getEditMode())) {
return new SQLEditMode(this);
}
else if (EDIT_MODE_DESIGN.equals(getEditMode())) {
return new DesignEditMode(this);
}

return new NullEditMode(this);
}

说明:调用者无需了解具体的类型,由View自身作判断,返回EditMode接口,从而实现多态调用。

ViewProcessBean代码片段:

重构前-版本1503:

public String expDocToExcel(String viewid, WebUser user, ParamsTable params) throws Exception {
if (view.getEditMode().equals(View.EDIT_MODE_DESIGN)) {
datas = dp.queryBySQLPage(sql, params, tempPage, LINES, user.getDomainid());
}
else if (view.getEditMode().equals(View.EDIT_MODE_CODE_DQL)) {
datas = dp.queryByDQLPage(dql, params, tempPage, LINES, user.getDomainid());
}
else if (view.getEditMode().endsWith(View.EDIT_MODE_CODE_SQL)) {
datas = dp.queryBySQLPage(sql, params, tempPage, LINES, user.getDomainid());
}
}

重构后-版本1566:

public String expDocToExcel(String viewid, WebUser user, ParamsTable params) throws Exception {
datas = view.getEditModeType().getDataPackage(params, tempPage, LINES, user, currdoc);
//其他业务逻辑
}

5.结语

由上述案例可看到,引入subclass代替type code可以大大减少if-else判断,而且可以把责任内聚到每种type中,使代码结构更清晰易懂。

6.其他

在本案例中引入了空类型概念,即NullEditMode,代码如下:

/**
*
*
@author nicholas zhen
*
*/

public class NullEditMode extends AbstractEditMode implements EditMode {

public NullEditMode(View view) {
super(view);
}

public String getQueryString(ParamsTable params, WebUser user, Document sDoc) {
return "";
}

public DataPackage getDataPackage(ParamsTable params, WebUser user, Document doc) throws Exception {
return new DataPackage();
}

public DataPackage getDataPackage(ParamsTable params, int page, int lines, WebUser user, Document doc) throws Exception {
return new DataPackage();
}

public long count(ParamsTable params, WebUser user, Document doc) throws Exception {
return 0;
}
}
 

说明:空类型保证了调用每个方法都有默认值返回,而不需要进行非空判断,当View没有类型时,即返回默认的空类型



专家视角看IT与架构
软件架构设计
面向服务体系架构和业务组件
人人网移动开发架构
架构腐化之谜
谈平台即服务PaaS


面向应用的架构设计实践
单元测试+重构+设计模式
软件架构师—高级实践
软件架构设计方法、案例与实践
嵌入式软件架构设计—高级实践
SOA体系结构实践


锐安科技 软件架构设计方法
成都 嵌入式软件架构设计
上海汽车 嵌入式软件架构设计
北京 软件架构设计
上海 软件架构设计案例与实践
北京 架构设计方法案例与实践
深圳 架构设计方法案例与实践
嵌入式软件架构设计—高级实践
更多...