Time of Update: 2018-12-07
某個函數需要從調用端得到更多資訊。為此函數添加一個對象參數,讓該對象帶進函數所需資訊。動機:Add Parameter (添加參數)是一個很常用的重構手法。使用這項重構的動機很簡單:你必須修改一個函數,而修改後的函數需要一些過去沒有的資訊,因此你需要給該函數添加一個參數。
Time of Update: 2018-12-07
你有一個數組,其中的元素各自代表不同的東西。以對象替換數組,對於數組中的每個元素,以一個欄位來表示。動機:數組時一種常見的用以組織資料的結構。不過,它們應該只用於“以某種順序容納一組相似對象”。有時候你會發現,一個數組容納了多種不同對象,這會給使用者帶來麻煩,因為他們很難記住像“數組的第一個元素是人名”這樣的約定。對象就不同了,你可以運用欄位名和函數名來傳達這樣的資訊,因此你無需死記它,也無需依賴注釋。而且如果使用對象,你還可以將資訊封裝起來。並使用 Move Method
Time of Update: 2018-12-07
你有一些子類,其中相應的某些函數以相同的順序執行類似的操作,但各個操作的細節不同。將這些操作分別放進獨立的函數中,並保持它們都有相同的簽名,於是原函數也就變得相同了,然後將原函數上移至超類。動機:繼承是避免重複行為的一個強大工具。無論何時,只要你看見2個子類之中有類似的函數,就可以把它們提升到超類。但是如果這些函數並不完全相同該這麼辦?仍有必要盡量避免重複,但又必須保持這些函數之間的實質差異。
Time of Update: 2018-12-07
某個函數返回一個特定的代碼,用以表示某種錯誤情況。改用異常。動機:程式中發現錯誤的地方,並不一定知道如何處理錯誤。當一段子程式發現錯誤時,它需要讓它的調用者知道這個錯誤,而調用者也可能將這個錯誤繼續沿著調用鏈傳遞上去。許多程式都使用特殊輸出來表示錯誤。 可以使用更好的錯誤處理方式:異常。它清楚地將“普通程式”和“錯誤處理”分開了,這使得程式更容易理解:代碼的可理解性應該是我們追求的目標。
Time of Update: 2018-12-07
你有一個複雜的條件陳述式。從if、then、else三個段落中分別提煉出獨立函數。動機:程式之中,複雜的條件邏輯是最常導致複雜度上升的地點之一。你必須編寫代碼來檢查不同的條件分支、根據不同的分支做不同的事,然後,你很快就會得到一個相當長的函數。大型函數自身就會使代碼的可讀性下降,而條件邏輯則會使代碼更難閱讀。在帶有複雜條件邏輯的函數中,代碼(包括檢查條件分支的代碼和真正實現功能的代碼)會告訴你發生的事,當常常讓你弄不清為什麼會發生這樣的事,這就說明代碼的可讀性的確大大降低了。
Time of Update: 2018-12-07
函數本體不再需要某個函數。將該參數去除。動機:程式員可能檢查添加參數,卻往往不願意去掉它們。他們打的如意算盤是:無論如何,多餘的參數不會引起任何問題,而且以後還可能用上它。 參數代表著函數所需的資訊,不同的參數值有不同的意義。函數調用者必須為每一個參數操心該傳什麼東西進去。如果你不去掉多餘參數,就是讓你的每一位使用者多費一份心。是很不划算的,更何況“去除參數”是非常簡單的一項重構。
Time of Update: 2018-12-07
函數對某個類的興趣高過對自己所處的類,通常的焦點就是資料,某個函數為了計算某個值,從另一個對象那兒調用幾乎半打的存取子。這時一個運用 Move Method (搬移函數)把它移到自己該去的地方。有時候函數中只有一部分受這種依戀之苦,這時候使用Extract Method (提煉函數)把這部分提煉到獨立函數中,再使用Move Method (搬移函數)帶它去它的夢中家園。
Time of Update: 2018-12-07
函數的名稱未能揭示函數的用途。修改函數名稱。動機:大力提倡的一種編程風格是:將複雜的處理分解成小函數。但是,如果做得不好,這會使你費盡周折卻弄不清楚這些小函數各自的用途。要避免這種麻煩,關鍵就在於給函數起一個好名稱。函數的名稱應該準確表達它的用途。給函數命名有一個好辦法:首先考慮應該給這個函數寫上一句怎樣的注釋,然後想辦法將注釋變成函數名稱。
Time of Update: 2018-12-07
你有一個引用對象,很小且不可變,而且不易管理。將它變成一個值對象。動機:要在引用對象和值對象之間做選擇,有時並不容易。做出選擇後,你常會需要一條回頭路, 如果引用對象開始變得難以使用,也許就應該將它改為值對象。引用對象必須被某種方式控制,你總是必須向其控制者請求適當的引用對象。它們可能造成記憶體地區之間錯綜複雜的關聯。在分布式和並發系統中,不可變的值對象特別有用,因為你無需考慮它們的同步問題。
Time of Update: 2018-12-07
平行繼承體系其實是散彈式修改(Shotgun Surgery)的特殊情況。在這種情況下,每當你為某個類增加1個子類,必須也為另一個類相應增加1個子類。如果你發現某個繼承體系的類名首碼和另一個繼承體系的類名首碼完全相同,便是聞到了這種壞味道。 消除這種重複性的一般策略是:讓一個繼承體系的執行個體引用另一個繼承體系的執行個體。如果再接再厲運用 Move Method (搬移函數)和Move Field (搬移欄位),,就可以將引用端的繼承體系取消。
Time of Update: 2018-12-07
你的程式中,有個函數與其所駐類之外的另一個類進行更多的交流:調用後者,或被後者調用。在該函數最常用引用的類中建立一個有著類似行為的新函數。將舊函數編程一個單純的委託函數,或是將舊函數完全移除。 動機:“搬移函數”是重構理論的支柱。如果一個類有太多行為,或如果一個類與另一個類有太多合作而形成高度耦合,就需要搬移函數。通過這種手段,可以使系統中的類更簡單,這些類最終也將更乾淨利落的實現系統交付的工作。
Time of Update: 2018-12-07
某個函數返回的對象,需要由函數調用者執行向下轉型(downcast)。將向下轉型動作移到函數中。動機:向下轉型也許是無法避免的,但你仍然應該儘可能少做。如果你的某個函數返回一個值,並且你知道所返回的物件類型比函數簽名所昭告的更特化,你便是在函數使用者身上強加了非必要的工作。這種情況下你不應該要求使用者承擔向下轉型的責任,應該盡量為他們提供準確的類型。
Time of Update: 2018-12-07
你的各個子類的唯一差別只在“返回常量資料”的函數身上。修改這些函數,使它們返回超類中的某個(新增)唯讀,然後銷毀子類。動機:建立子類的目的,是為了增加新特性或變化其行為。有一種變化行為被稱為“常量函數”,它們會返回一個硬式編碼值。這東西有其用途:你可以讓不同的子類中的同一個訪問函數返回不同的值。你可以在超類中將訪問函式宣告為抽象函數,並在不同子類中讓它返回不同的值。
Time of Update: 2018-12-07
面對一個調用者可以預先檢查的條件,你拋出一個異常。修改調用者,使它在調用函數之前先做檢查。動機:異常的出現是程式語言的一大進步。但是,就像許多好東西一樣,異常會被濫用,從而變得不再讓人愉快。“異常”只應該被用於異常的、罕見的行為,也就是那些產生意料之外的錯誤的行為,而不應該成為條件檢查的替代品。如果你可以合理期望調用者在調用函數之前檢查某個條件,那麼就應該提供一個測試,而調用者應該使用它。
Time of Update: 2018-12-07
你有一系列條件測試,都得到相同結果。將這些測試合并為一個條件運算式,並將這個條件運算式提煉為一個獨立函數。動機:有時你會發現這樣一串條件檢查:檢查條件各不相同,最終行為卻一致。如果發現這種情況,就應該使用“邏輯或”和“邏輯與”將它們合并為一個條件運算式。
Time of Update: 2018-12-07
某個函數既返回對象狀態值,又修改對象狀態。建立2個不同的函數,其中一個負責查詢,另一個負責修改。動機:如果某個函數只是向你提供一個值,沒有任何看得到的副作用,那麼這是個很有價值的東西。你可以任意調用這個函數,也可能把調用動作搬到函數的其他地方。明確表現出”有副作用”與“無副作用”2種函數之間的差異,是個很好的想法。任何有傳回值的函數,都不應該有看得到的副作用。有些程式員甚至將此作為一條必須遵守的規則。
Time of Update: 2018-12-07
2個類都需要使用對方特性,但其間只有1條單向串連。添加1個反向指標,並使修改函數能夠同時更新2條串連。動機:開發初期,你可能會在2個類之間建立1條單向串連,使其中一個類可以使用另一個類。隨著時間推移,你可能發現被引用類需要得到其引用者以便進行某些處理。也就是說它需要一個反向指標。但指標是一種單向串連,你不可能反向操作它。通常你可以繞道而行,雖然會耗費一些計算時間,成本還算合理,然後你可以在被引用類中建立一個函數專門負責此行為。但是,有時候想繞過這個問題並不容易,此時就需要建立雙向參考關聯性,或
Time of Update: 2018-12-07
常常可以在很多地方看到相同的3、4項資料:2個類中相同的欄位、許多函數簽名中相同的參數。這些總是綁在一起出現的資料真應該擁有屬於它們自己的對象。首先找出這些資料以欄位形式出現的地方,運用Extract Class (提煉類)將它們提煉到一個獨立對象中。然後將注意力轉移到函數簽名上,運用Introduce Parameter Object (引入參數對象)或Preserve Whole Object
Time of Update: 2018-12-07
某個類沒有做太多事情。將這個類的所有特性搬移到另一個類中,然後移除原類。動機:Inline Class (將類內聯化)正好於Extract Class (提煉類)相反。如果一個類不再承擔足夠責任、不再有單獨存在的理由(這通常是因為此前的重構動作移走了這個類的責任),就挑選這個“萎縮類”的最頻繁的使用者(也是個類),以Inline Class
Time of Update: 2018-12-07
在條件運算式的每個分支上有著相同的一段代碼。將這段重複代碼移到條件運算式之外。動機:一組條件運算式的所有分支都執行了相同的某段代碼。你應該將這段代碼搬移到運算式外面。這樣,代碼才能更清楚地表明哪些東西隨條件變化而變化、哪些東西保持不變。做法:1、鑒別出“執行方式不隨變化而變化”的代碼。 2、如果這些共通代碼位於條件運算式起始處,就將它移到條件運算式之前。 3、如果這些共通代碼位於條件運算式的尾端,就它移到條件運算式之後。