重構,其實很簡單,它的目的就是讓程序變得更容易被理解,更具有可維護性,結構更合理。重構應該是我們平時寫代碼過程中必不可少的一部分,比如給函數起了一個更好的名字、把大函數拆分成幾個小函數等都屬於重構。重構的經典書籍包括Martin Flower的《重構-改善既有代碼的設計》、Joshua Kerievsky的《重構與模式》,本系列的所謂36計是我多年來使用最為頻繁的重構策略和編碼准則,有自己總結的,也有書上提到過的,希望對大家能有所幫助。
第一計:參數列表對象化
公有函數的參數應盡可能保持不變,因為很多地方都會調用它,修改參數后需要修改它的調用處,另外,它的參數列表不宜過長,數量盡量保持在5個以內,長參數列表會增加該函數的調用難度。對於參數較多或者參數經常變化的公有函數,較好的辦法是引入參數對象,即該函數的參數只有一個,它就是參數對象,具體的參數都在該對象中聲明,為函數引入參數對象有以下幾個好處:
1、保持函數接口的不變性,修改函數參數只需修改參數對象中的成員變量。
2、調用方便,調用方不用再關心參數的順序。
以下代碼片段是一個添加用戶函數的聲明:
public long insertUser(String name,int age,String email,String address,String phone,String birthDay)
每當添加或刪除用戶的字段后都要修改insertUser的參數列表,調用者也需要修改,而且參數較多時,不容易記憶。
以下是引入參數對象后的形式:
public class UserParam{ public String name; public int age; public String email; public String address; public String phone; public String birthDay; } public long insertUser(UserParam user);
第二計:條件運算符賦值代替if else賦值
對於根據條件為變量賦值的情況,可以有兩種方式,一種是通過if-else:
int value; if(condition) value = 1; else value = 2;
另一種是通過條件運算符:
int value = condition ? 1 : 2;
第二種方式明顯要比第一種方式好,但是很多人卻鍾愛第一種方式,可能是if-else習慣了。
第三計:節約使用系統資源
即使在寫代碼時,我們也應該養成“節儉”的習慣,不要隨便浪費系統提供的資源,對於那些較占用空間、影響性能的對象,應該直到真正要用的時候才創建或者初始化,因此在提供這些對象的函數實現中,盡量采用如下形式:
// 管理數據庫連接的類 public class DataBaseConnectionHolder{ private Connection conn; public Connection getConnection(){ if(conn == null){ conn = new Connection(); conn.init(); } return conn; } }
第四計:為接口引入抽象版本
在聲明一個新的接口時,不能保證該接口不會被修改,有可能會經常修改它,每一次修改接口都要修改相應的實現類,如果某個接口是公共庫的一部分,那么修改接口的代價是較大的,用到該接口的所有程序都需要重新修改、編譯...,通過為接口引入抽象版本可以解決這個問題,例如為下面的接口增加一個抽象類:
public interface Widget{ public void draw(); public void layout();
public void invalidate(); public void show(); }
public abstract class AbstractWidget implements Widget{ public abstract void draw(); public void layout(){}; public void invalidate(){}; public void show(){}; }
這樣Widget的實現類可以直接從AbstractWidget繼承,如果要修改Widget接口,則只需要修改AbstractWidget即可,對於其他實現類沒有影響。
第五計:消滅魔法數
編程新手一般都會直接將表示類型或狀態的數字直接寫在處理邏輯中,代碼的作者能明白該數字所表示的含義,但其他人讀到這段代碼時就很有可能看不懂了。即使代碼的作者再過一段時間來看這部分代碼,也可能會忘記該數字的含義,而且,當我們要修改魔法數的值時,過程是很繁瑣的,很有可能會有所遺漏,所以,最好的辦法是徹底消滅程序中的所有魔法數,通過常量定義、枚舉等方式來避免魔法數的出現。
第六計:使用斷言、異常確保實現的正確性
使用斷言的目的是告知其他程序員代碼中某處必須要遵守的規矩,它是debug版本中的一種確保程序實現正確性的手段,在正式發布的版本中,斷言是不起作用的。在java中,啟用斷言需要增加一個編譯選項,不過可以通過拋出異常來達到相同目的,使用異常比斷言要危險,因為在程序的正式發布版本中會引起崩潰,不過有時候崩潰總比程序的詭異行為更好,例如:
// 表示集合的類 public class Collection{ // 添加元素到集合中 public void addElement(Element e){}; // 獲取指定位置的元素 public void getElement(int index){}; } // 表示只讀集合的類 public class ReadOnlyCollection extends Collection{ // 添加元素到集合中 public void addElement(Element e){ throw new UnsupportedOperationException("只讀集合,不允許添加元素"); } // 獲取指定位置的元素 public void getElement(int index){}; }
調用ReadOnlyColletion派生類必須遵守規矩:不能調用addElement,否則拋出異常干掉程序!