日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

重构36计

發布時間:2024/4/17 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 重构36计 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

http://blog.csdn.net/m13666368773/article/details/7472201

重構,其實很簡單,它的目的就是讓程序變得更容易被理解,更具有可維護性,結構更合理。重構應該是我們平時寫代碼過程中必不可少的一部分,比如給函數起了一個更好的名字、把大函數拆分成幾個小函數等都屬于重構。重構的經典書籍包括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; elsevalue = 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;}}

?

另外,我們可以通過引入緩存機制(如對象池)來充分利用系統資源,可以參看這篇文章:GoF著作中未提到的設計模式(5):Object Pool

?第四計:為接口引入抽象版本

??在聲明一個新的接口時,不能保證該接口不會被修改,有可能會經常修改它,每一次修改接口都要修改相應的實現類,如果某個接口是公共庫的一部分,那么修改接口的代價是較大的,用到該接口的所有程序都需要重新修改、編譯...,通過為接口引入抽象版本可以解決這個問題,例如為下面的接口增加一個抽象類:

?

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,否則拋出異常干掉程序!

?

第七計:串聯函數調用

當一個類的大部分函數被較為頻繁地調用,并且包含連續性地調用,那么可以考慮為這個類中那些沒有返回值的函數增加返回值,即返回對象本身,這樣就可以串聯函數調用,使用起來較為方便,舉個例子:

// 表示用戶的類 public class User{public void setName(String name);public void setAge(int age);public void setPhoneNumber(int phoneNumber); }

下面是不使用串聯函數調用的情況:

?

User user = new User(); user.setName("West_Link"); user.setAge(3); user.setPhoneNumber(122333);

?

下面是使用串聯函數調用的情況:

?

User user = new User().setName("West_Link").setAge(3).setPhoneNumber(123333);

?

只需要為那些函數增加一個User對象的返回值即可,如下:

public User setName(String name){ this.name = name; return this; }

?

第八計:臨時變量在用到時才聲明

?很多人喜歡在函數的開頭把所有要用到的臨時變量都聲明了,我認為這種方式有以下幾個缺點:

1、不利于代碼的閱讀,需要經常在變量的使用處和變量的聲明處跳轉,不方便。
2、容易造成資源的浪費,因為有些對象的初始化是比較耗費資源的,而函數可能在用到該對象之前返回。
3、不利于函數的拆分。
所以,我們應該盡可能降低臨時變量的作用域,那樣它能“搗亂“的范圍就被縮至最小了。

?

第九計:保持類的公有函數粒度最小化

? 一個類的公有函數不應該過多,那樣會使類變得臃腫、不易使用,我認為最佳狀態是絕大部分公有函數不能被拆分,也就是說,不存在那些通過其他公有函數也能達到目的的函數,例如下面的類:

?

public class StringArray{// 獲取數組的大小public int getSize();// 判斷數組是否為空public boolean isEmpty();// 將數據添加到數組的某個索引public void add(String value,int index);// 將數據添加到數組的末尾public void addToLast(String value);// 將數據添加到數組的起始位置public void addToFirst(String value); }

?

StringArray其實只需要兩個公有函數即可,即getSize和add,因為isEmpty可以通過getSize來達到相同的目的:getSize() == 0。類似地,addToLast、addToFirst也可以通過add來實現。不過,如果類的公有函數比較少,而且類似isEmpty類似的需求經常被用到,那么保留這些公有函數還是值得的。

?

第十計:將可訪問性降至最低

? 面向對象中的封裝性使得我們可以隱藏類或者接口的實現細節,所以,為了讓程序更易維護、接口或者類的使用更加簡單,我們應該盡可能降低成員變量或者成員函數的可訪問性,輔助函數一定要聲明為私有的,確保只將接口函數聲明為公有的,如果該輔助函數可能會被子類用到,則可以聲明為保護的。輔助類也應聲明為私有的,對于成員變量則一定要聲明為私有的,只提供必要的set或者get函數。總之,當我們增加新的類、成員變量、成員函數時,一定要合理地設置可訪問性,暴露給外界的越少越好。

?

第十一計:合并條件減少嵌套

? 條件判定如果嵌套的太多會大大降低程序的可讀性,很容易滋生Bug,例如:

? ?

if(a){if(b || c){if(d){...}} }

?可以合并成:

if(a && (b || c) && d){... }

?

?

第十二計:循環中早用continue減少條件嵌套

?除了通過合并條件來減少嵌套層次外,在for或者while循環中,可以用continue來減少條件嵌套,例如:

?

for(int i=0; i<100;i++){if(a){if(b){if(c){...}}} }

?

這段代碼的嵌套深度是4,使用continue可以大大減少嵌套層次:

?

for(int i=0; i<100;i++){ if(!a) continue; if(!b) continue; if(!c) continue; ...}

?

第十三計:為集合類型的成員變量提供增刪改查函數

??對于集合類型的成員變量,直接提供一個函數將其暴露出去是不夠妥當的,缺點包括以下幾點:

? 1、添加集合元素時無法校驗它的合法性,例如類型是否符合要求、該元素是否已存在等。
? 2、無法控制集合的容量,集合的容量可能是動態分配的。
? 所以,更好的做法是為集合類型的成員變量提供增刪改查等函數,例如某個類中有名為userList的數組,則可以為它增加幾個函數:

?

public void add(User user){if(user == null) // 如果對象為不合法,則不添加return;if(userList == null) // 如果集合還未初始化,則新建userList = new ArrayList();if(isUserExisted(user)) // 如果該用戶已經存在,則不添加return;// 將該用戶添加到集合中 userList.add(user); }public void delete(User user){if(userList == null)return;userList.remove(user); }

?

第十四計:避免一個臨時變量充當多種角色

? 當在函數中聲明一個臨時變量的時候,其實已經設定了該變量的角色,這或多或少能從它的命名中看出來,例如下面變量的聲明:

?

String userName = null;??

?

? 可以看出這個臨時變量是存儲用戶名用的,有些人認為聲明多個臨時變量會浪費空間,所以在函數中會多次使用該變量,例如用這個變量存儲了用戶的密碼:

?

userName = “*******”;

??

?這種方式很容易引入Bug,而且降低了可理解性。因此,一個變量應該只充當一種角色。

?

第十五計:引入NULL Object來避免大量的對象合法性判斷

??當我們獲得對象的引用后,在調用該對象的函數前一般都會檢查它是否為NULL來避免程序崩潰,這樣也會導致程序中出現大量類似下面的代碼段:

??

EventRecorder recorder = EventRecorderFactory.getRecorderByType(0); if( recorder ==null ){Log.error("Recorder對象為空");lastErrorCode =0; } else{recorder.record("記錄點啥..."); }

?

而NULL Object模式則可以避免這種情況,具體內容請參見:GoF著作中未提到的設計模式(3):Null Object

?

第十六計:函數命名有語法

??大部分函數的命名盡量采用動詞+名詞的形式,并使其具有自注釋性,例如:findUserById,從函數名中不僅能看出函數的功能,甚至連參數也能猜出來,另外,有些命名方式是有一定意義的,例如作為回調的函數一般以on開頭,如:onUserPasswordChanged,說明該函數會在用戶密碼變化時被調用。對于返回布爾值的函數盡量采用疑問句式,如:isNameValid。

?

第十七計:去除只是內部狀態不同的派生類

??當某些派生類與父類相比只是狀態不同時,那就應該考慮去掉這些派生類,把這些狀態作為父類的成員變量,并且可以為原來派生類所表示的對象準備一些構造函數或者工廠方法,例如下面表示員工的類:

?

public abstract class Employee{private int id;private String name;// 獲取薪水public abstract int getSalary();// 獲取是否有解雇員工的權利public abstract boolean canFireOthers(); }public class Programmer extends Employee{public int getSalary(){return 5000;}public boolean canFireOthers(){return false;} }public class Manager extends Employee{public int getSalary(){return 10000;}public boolean canFireOthers(){return true;} }

可以看出,Employee的派生類實質上只是狀態與父類不同,應該將它們合并為一個類:

??

public class Employee{private int id;private String name;private int salary;private boolean canFireOthers;public static Employee newProgrammer(String name){return new Employee(name,5000,false);}public static Employee newManager(String name){return new Employee(name,10000,true);}public Employee(String name,int salary,boolean canFireOthers){this.name = name;this.salary = salary;this.canFireOthers = canFireOthers;}// 獲取薪水public int getSalary(){return salary;}// 獲取是否有解雇員工的權利public boolean canFireOthers(){return canFireOthers;} }

?

第十八計:少用標記變量

??標記變量一般都是布爾類型的變量,主要用來在某類事件或者操作發生后做個標記,然后其他地方會用到這個標記,用完之后很可能會重置這個標記到初始狀態。少量并恰當合理地使用標記變量可以達到很好的效果,能解決一些難題,不過,用的多了就會出亂子,尤其是一個類中有多個成員變量是標記變量,成員函數的實現不得不“看它們的眼色行事“了,它們所產生的標記值的組合會讓實現者越來越頭大,它們的同時存在增加了程序的復雜性。所以,當類中出現多個標記變量而隱約感覺到不對頭時,應該果斷干掉它們,然后認真分析處理邏輯是否存在問題,避免再次引入標記變量。

?

?

第十九計:避免類的臃腫

??在我接觸過的大部分項目中,總會有一個“大胖子“類特別惹眼,一般來說,這“大胖子“實際上是整個系統的核心類之一,之所以“胖“,主要原因是很多人都會把自己需要的函數加到這個類中,卻沒有人主動請纓來為它“減肥“。可以通過以下幾種方式來為它“瘦身”:

??1、按照某種特性(如功能、類型等)將這個類拆分成多個類。

? 2、合并冗余函數,保持函數粒度的最小化。

? 3、去除重復代碼。

??如果實在不能再“瘦”了,那就通過實現相應的接口,讓它“看上去很瘦“,舉個例子:

??

public class BigBoy{public void foo1();public void foo2();public void foo3();public void foo4();public void foo5();public void foo6();public void foo7();public void foo8();public void foo9();// 還有很多... }

?

這個類有很多函數,這讓類的使用者很頭疼,沒辦法,它不能再“瘦“了,不過,我們可以根據某種特性把這些函數抽象成多個接口,例如foo1、foo2、foo3可以抽象成一個接口:

public interface LittleBoy{public void foo1();public void foo2();public void foo3(); }

?

然后讓BigBoy實現這個接口,并提供一個將BigBoy變成LitterBoy的函數:

?

?

public class BigBoy implements LittleBoy{public LittleBoy asLittleBoy{)return this;}public void foo1();public void foo2();public void foo3();public void foo4();public void foo5();public void foo6();public void foo7();public void foo8();public void foo9();// 還有很多... }

?

這樣,類的使用者得到將是“瘦版“的BigBoy,使用難度大大降低了,因此,對于需要使用該類所有函數中某個子集的用戶,我們可以提供一個包含該函數子集的接口實現對象即可。

?

第二十計:保持代碼風格的一致性

??程序員或多或少都有各自的代碼風格,當我們看和自己風格不同的人寫的代碼時都覺得有點別扭,甚至會影響讀代碼的流暢性,記得以前有個同事把for語句當if語句用,像下面這樣,太個性了,看他的代碼真有點頭疼。

??

for(;value>10;){... }

整個程序保持一致的代碼風格還是比較重要的,如果看著就像一個人寫那就太到位了,所以,最好能在項目初期就統一程序的命名規范、通過邏輯處理規范、注釋規范等。

?

第二十一計:成員變量要封裝

??大部分情況下,類中的成員變量都應該被聲明為私有的,為那些需要被其他類訪問的變量增加set或者get函數。如果成員變量聲明為公有的,那么類就失去了對它們的控制權!封裝成員變量有以下幾個好處:

? 1、在為成員變量賦值時進行有效性校驗或其他預處理操作。

? 2、在返回成員變量的值時進行二次包裝,當該變量不可用或未初始化時返回默認值。

? 為成員變量分別增加set和get函數是一件挺繁瑣的事情,很多人嫌麻煩,覺得公有成員變量更方便,不過從可維護性和可擴展性的角度看,添加set和get是值得的,Eclipse中可以自動生成set和get函數的,非常方便。

?

第二十二計:用自注釋性變量代替復雜條件

??嵌套層次較深的IF判斷、沒有注釋的復雜條件大大增加了程序的邏輯復雜性,嚴重降低代碼的可讀性。對于子條件較多的條件判定,可以為各個子條件引入具有自注釋性的臨時變量來降低復雜性。例如下面是判斷登錄用戶是否能進入網站的后臺管理界面:

??

if(userName!=null && userName.equals(name) && (userState != INACTIVE || userState != DELETE) && userPassword!=null && userPassword.equals(password) && (userGroup == "Manager" || userGroup == "Root")){... }

?

?

? 下面是修改后的版本:

boolean isUserNameValid = userName!=null && userName.equals(name); boolean isUserActive = userState != INACTIVE || userState != DELETE;boolean isUserPasswordCorrect = userPassword!=null && userPassword.equals(password);boolean isUserHasAuth = userGroup == "Manager" || userGroup == "Root";if(isUserNameValid && isUserActive && isUserPasswordCorrect && isUserHasAuth){...}

?

第二種方式不僅使條件判斷更具有可讀性,還能重用子條件。

?

第二十三計:避免重復代碼

??重復代碼是破壞程序可維護性的重量級選手之一,大量的重復代碼會使代碼量膨脹,修改重復的代碼也很繁瑣,改了一處后必須同時修改和它重復的代碼,因此非常引入Bug,當有人修改了某處代碼而忘記修改其他除重復的代碼,那么Bug就出現了。所以,一旦要拷貝某段代碼,請先考慮把這段代碼通用化。

?

第二十四計:增加注釋

??記得在第一個公司工作的時候,公司很多程序員的代碼注釋率在40%左右,一般都是先寫注釋,然后緊接著寫代碼,因為注釋也是一種文檔。很多人覺得寫注釋浪費時間或者沒有必要,所以他們的代碼中沒有綠色,或者只是星星點點,如果代碼有一定的復雜性,那么其他人看這部分代碼可能會比代碼作者要費勁的多,注釋就是幫助別人快速理解自己寫的代碼。

第二十五計:函數體最多不超過100行

??記得以前看過一個函數有9000多行,很壯觀啊,從那以后看到長函數時也不奇怪了,我認為過長函數的主要缺點是:

1、嚴重影響代碼的閱讀,使用到某個變量的地方可能間隔幾百甚至上千行,如果if-else嵌套層次較多的話那就更噩夢了。

2、不利于代碼的重用,短小而獨立的邏輯處理單元更容易被重用,而過長的代碼段則需要經過進一步分解才行。

我覺得函數最好不要超過100行,對于過長的函數要盡可能地進行分解,如果實在不能分解,那么就通過注釋的方式增加該函數處理步驟的說明,例如:

?

public void foo(){// 1、驗證參數、內部狀態的有效性 ... // 2、開始傾斜角度 ... // 2.1 計算角度1 ... // 2.2 計算角度2 ... // 3、輸出計算說明書 ... }

?

第二十六計:使用語言的修飾符確保變量的不可變性

??當聲明一個變量時,如果能十分確定該變量不會被修改或者不應該被修改,那最好把它聲明為不可變的,如使用Java中的final、C++中的const修飾符,這樣可以防止本該不變的變量被意外地修改。

?

第二十七計:對象狀態共享

??大量對象的同時存在會占用系統寶貴的內存,如果這些對象中某些狀態是相同的,那么可以將這些狀態提取出來讓所有需要它的對象共享,這可以大大減少冗余對象,從而達到節省內存的目的,設計模式中的Flyweight模式可以解決這個問題。

?

第二十八計:用對象代替普通常量

??由于普通常量本質上是一個簡單的數字或者字符串,當我們錯誤地將某個類別的常量在另一個類別的常量的場景中使用時,就會產生問題,但是編譯器并不會提示有錯誤,所以,這可能是一個不小的隱患,例如:

?

// 表示用戶狀態的常量聲明 public static int USER_STATE_ACTIVE = 0; public static int USER_STATE_DELETE = 1;// 表示用戶角色的常量聲明 public static int USER_ROLE_NORMAL = 2; public static int USER_ROLE_MANAGER = 3;// 下面用戶是否被激活的判斷 if(userState == USER_ROLE_NORMAL){}

這個判斷本應該使用USER_STATE_ACTIVE和USER_STATE_DELETE兩個常量之一,卻意外地使用了其他常量,可能直到Bug產生后才能被發現。

可以使用對象常量來避免這種情況,例如:

public class State{private int state;public State(int s){state = s;} }// 表示用戶狀態的常量聲明 public static State USER_STATE_ACTIVE = new State(0); public static State USER_STATE_DELETE = new State(1);public class Role{ private int role; public Role(int r){role = r;} }// 表示用戶角色的常量聲明 public static Role USER_ROLE_NORMAL = new Role(2); public static Role USER_ROLE_MANAGER = new Role(3);

?

下面的判斷是無法通過編譯的,因為userState是State類型的。

if(userState == USER_ROLE_NORMAL){}

?

第二十九計:查詢函數中盡量不要有修改操作

??我們一般都是根據函數的名字來判斷它的功能,“表里不一“的函數可能會引起一些問題,例如我們調了一個查詢函數(獲取類的成員變量值的函數):getName(),但是它內部卻修改了其他成員變量的值,當查找Bug的原因時,很可能會忽略這個函數,從它的名字看,覺得它不會引起問題,到最后發現就是它搗的鬼,心里估計會罵這個函數的作者:他奶奶的,代碼如其人,表里不一!

?

第三十計:盡量封裝對象的創建過程

??本文之前曾提到過要盡量為成員變量增加set和get函數,主要目的是為了掌握成員變量的控制權,對象的創建過程也是如此,如果提供者掌握了對象創建過程的控制權,那么就可以屏蔽具體的實現類,并且任意修改對象的創建過程。?

第三十一計:置空不用的對象

在C++中,銷毀一個對象后,一定要把指針置為NULL,否則會出現野指針,最好寫成下面這樣,delete后立馬置為NULL,

delete pObject; pObject = NULL;

在Java中,當不再需要一個對象時,最好能把它置為null,這樣有利于垃圾回收。

?

第三十二計:善于利用接口

? 1、?回調型接口

? ??在C語言中,回調函數可以通過函數指針來實現,Java中沒有指針的概念,可以利用接口來達到同樣的目的,例如:

? ??

public interface Callback{public void onChanged();}public void execute(Callback callback){...callback.onChanged();...}

?

?2、標記型接口

這種類型的接口中不包含函數的聲明,即接口是空的,主要用來讓實現這個接口的類表明自身具有某種特性,起一個標記的作用,例如下面的接口:

public interface Millionaire{}public class Member extends User implements Millionaire{...}public boolean check(User user){if(user instanceOf Millionaire) // 這是百萬富豪,直接讓它passreturn true;...}

?

? 3、依賴注入型接口
? ? 這種接口一般用在工廠方法中,有選擇性地為要創建的對象注入對象引用或者數據,例如:

public interface DataAware{public void setData(Data data);}public interface DataBaseConnectionAware{public void setConnection(Connection conn);}// 用戶服務類public class UserService extends Service implements DataAware,DataBaseConnectionAware{public void setData(Data data);public void setConnection(Connection conn);}// 創建服務對象public Service createService(int type){Service service = null;if(type == USER){service = new UserService();if(service instanceOf DataAware) // 如果它想要數據,則注入數據對象 ((DataAware)service).setData(data);if(service instanceOf DataBaseConnectionAware) // 如果它想要數據庫連接,則注入連接對象 ((DataBaseConnectionAware)service).setConnection(conn);return service;}

?

?

?4、常量型接口

? 接口中定義的全是常量,這樣,相關類都可以實現這個接口,相當于把這些常量都定義在了這些類中,而其他類則可以通過常量接口來引用這些常量

?

?第三十三計:簡化類關系

? ?如果類之間的關系比較復雜,那么很可能是面向對象的設計沒有做好。一般來說,兩個類之間有雙向調用關系則說明它們在職責上的分配上不夠清晰,例如,一個類(A)頻繁地調用另一個類(B)的函數,而且A還把成員變量作為參數傳遞給B,那么大部分情況下就應該把那個函數定義在A中,從而切斷兩者之間的關系。另外,系統中有很多類對某些類的狀態變化或者事件感興趣,例如添加一個新用戶、網絡狀態的變化等,對于這樣的需求,最好通過各種消息機制來達到它們之間的解耦,如觀察者模式、發布訂閱模式等。發布訂閱模式可以參見這篇文章:GoF著作中未提到的設計模式(7):Publish-Subscribe

?

?第三十四計:用多態替換相似條件式

? 當一個類中有些函數的處理邏輯很相似,都是根據某個狀態值或者某個類型值而采取不同的行為,那么這個類就需要被拆分成多個類了,例如有一個表示形狀的類:?

??

public class Shape{private int type;public void draw(){if(type == 0) // 如果是矩形 drawRect();else if(type == 1) // 如果是圓形 drawCircle();else if(type == 2) // 如果是直線 drawLine();} public boolean isPointIn(float x, float y){if(type == 0) // 如果是矩形return isPointInRect(x,y);else if(type == 1) // 如果是圓形return isPointInCircle(x,y);else if(type == 2) // 如果是直線return isPointInLine();return false;} }

這兩個成員函數內部包含相似的處理邏輯,都是根據type的值做相應的處理,對于這種情況,應該充分利用面向對象中的多態性,下面是拆分成多個類的形式:

?

public abstract class Shape{public abstract void draw();public abstract boolean isPointIn(x,y); }public class Circleextends Shape{public void draw(){drawCircle();}public abstract boolean isPointIn(x,y){isPointInCircle(x,y);} }public class Rect extends Shape{public void draw(){drawRec();}public abstract boolean isPointIn(x,y){isPointInRect(x,y);} }public class Line extends Shape{public void draw(){drawLine();}public abstract boolean isPointIn(x,y){isPointInLine(x,y);} }

?

?

第三十五計:合理分層,分離界面顯示和業務處理邏輯

代碼的低耦合是保障軟件可維護性的關鍵因素之一,而分層是實現代碼低耦合的常用手段,也可以實現人員的有效分工,例如在企業級的Web開發中,一般都會劃分出以下幾個層次:
1、數據訪問層,完成數據庫的增刪改查,一般都使用ORM框架來實現,例如Hibernate、LINQ等。
2、業務服務層,完成所有的業務處理邏輯
3、請求控制層,處理客戶端請求,將業務交給服務層處理,把處理結果以某種形式(HTML、JSON等)返回給客戶端。
在本地應用程序的實現中也會有類似的分層,例如MFC中的文檔-視圖結構也是界面顯示和業務處理邏輯的分離,所以在代碼中,應該盡量避免界面對象和業務對象的交叉引用,尤其是對于有多種界面呈現方式的系統中。

?

第三十六計:判斷參數有效性

? ?函數參數有效性的判斷是函數實現中很重要的一部分,尤其是作為類的對外接口的公有成員函數,例如判斷參數中的對象是否為null、參數值是否合法等,這些工作應該在函數實現的最開始處完成,發現參數不合法時可以直接返回、拋出異常或者提供參數默認值,一定不要通過非代碼的方法(比如調用約定)來保證參數的的有效性,斷言也不能完全保證函數執行時用到的參數都是合法的,除非在程序發布時所有的公有函數都能被執行到,因此,參數有效性的判斷是函數實現中必不可少的部分。

?

總結

以上是生活随笔為你收集整理的重构36计的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。