以子類取代類型編碼
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
文章來源:http://www.cnblogs.com/obpm/archive/2010/07/13/1776856.html