重构实战案例一,以子类取代类型编码(原创)

以子类取代类型编码

 Replace Type Code with Subclasses

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没有类型时,即返回默认的空类型

 

原创人员:Nicholas

posted on 2010-07-13 23:22  obpm  阅读(1060)  评论(0编辑  收藏  举报