重構第六章讀書筆記(每次命名都不統一。。)

6.1 Extract Method(提煉函數)
解釋:有一段代碼可以被組織在一起並獨立出來。
處理機制:將這段代碼放進一個獨立的函數中,並讓函數名稱解釋該函數的用途。第三章代碼的壞味道,Long Method中也提到,讓小函數容易理解的關鍵在於有一個好的名字,讀者可以通過函數名來了解函數的作用。並且有一個原則,每當感覺需要用註釋來說明什麼的時候,就將需要說明的部分固定到一個函數中,並且給它取個好名字。
提煉函數的動機:函數過長或者一段代碼需要註釋才能讓人理解。
函數提煉的好處:1.提煉後的函數粒度小,被複用的機會更大;2.使高層函數讀起來像一系列註釋;3.函數的複寫更容易。
做法:1.創造新函數,根據函數的意圖命名函數;2.將需要提煉的代碼複製到新函數中;3.檢查代碼是否引用“作用於限於源函數”的變量;4.檢查是否有“僅用於被提煉代碼段”的臨時變量,如有,在目標函數中將它們生命爲臨時變量;5.檢查是否有任何局部變量的值被改變;6.將被提煉代碼段中需要讀取的局部變量當作參數傳遞給目標;6.在被提煉函數原來位置調用新函數;7.編譯,測試;
6.2 Inline Method(內聯函數)
解釋:一個函數的本體與名稱同樣清楚易懂,在該函數被調用的地方插入函數本體,移除調用。
動機:1.函數簡潔易懂,非必要的間接性讓人不舒服;2.需要將一羣組織不合理的小函數內聯到大函數中再提煉出合理的小函數,在實施Replace Method with Method Object(以函數對象取代函數)前這麼做會有不錯的效果;3.使用了太多間接層,只爲對另外一個函數的簡單委託,去除無用的間接層。
做法:1.檢查函數確定其不具有多態性(因爲如果有子類繼承,實施內聯函數後子類將無法覆寫);2.找出該函數所有被調用點,替換爲函數本體;3.編譯測試;4.刪除該函數定義。
6.3 Inline Temp(內聯臨時變量)
解釋:有一個臨時變量只被簡單表達賦值一次,而且妨礙了其他重構手法。
處理機制:將所有對該變量的引用動作都替換爲對它賦值的表達式自身。
動機:多半作爲Replace Temp with Query(以查詢取代臨時變量)的一部分,正真的動機出現在後者中。唯一單獨使用的情況是某個臨時變量被賦予某個函數調用的返回值。如果這個臨時變量妨礙其他重構手法,就應該將它內聯化。
做法:1.檢查給臨時變量賦值的語句,確保等號右邊沒有副作用;2.將這個臨時變量聲明爲final,然後編譯(可以檢查該臨時變量是否只被賦值一次);3.將所有引用點替換爲“爲臨時變量賦值”的表達式;4.每次修改完編譯測試;5.修改完所有引用點,刪除該臨時變量聲明和賦值語句;6.編譯測試。
6.4 Replace Temp with Query(以查詢取代臨時變量)
解釋:程序以一個臨時變量保存某一表達式的運算結果。將這個表達式提煉到一個獨立函數中。將這個臨時變量的所有引用點替換爲對新函數的調用。此後新函數就可以被其他函數使用。
動機:臨時變量是暫時的,只能在所屬函數內使用,如果將臨時變量替換爲查詢,同一個類中的所有函數都可以獲得這個信息。
做法:1.找出只被賦值一次的臨時變量並且聲明爲final;2.編譯(檢查以確保該臨時變量只被賦值一次);3.將“對該臨時變量賦值”的語句等號右側部分提煉到一個獨立函數中(聲明爲private,後期可能有更多類使用它;確保提煉出來的函數無副作用);4.編譯測試;5.在該臨時變量上實施Inline Temp(內聯臨時變量)。
6.5 Introduce Explaning Variable(引入解釋性變量)
解釋:有一個複雜的表達式,將該複雜表達式(或者其中一部分)的結果放進一個臨時變量,以此變量的名稱來解釋表達式的用途。
動機:表達式可能複雜難以閱讀,臨時變量代替原有表達式,取一個好的名字,例如放入條件語句中,可以方便代碼閱讀與理解。條件語句中,引入解釋性變量顯得很有價值,在較長的算法中可以用臨時變量來解釋每一步算法的意義。
侷限:臨時變量只在它所處的函數中有意義,更多時候使用提煉函數,當提煉函數難以進行時候,就引入解釋性變量。
做法:1.聲明final臨時變量,將複雜表達式中一部分運算結果賦值給它;2.用聲明的臨時變量代替對應的表達式;3.編譯測試。
6.6 Split Temporary Variable(分解臨時變量)
解釋:程序有某個臨時變量被賦值超過一次,它既不是循環變量也不被用於收集計算結果。——針對某次賦值,創造一個獨立、對應的臨時變量。
動機:臨時變量用途不同,某些用途會導致臨時變量被多次賦值,同一個臨時變量承擔兩件不同的事情,會令代碼閱讀者糊塗,因此,如果臨時變量承擔多個責任,它就應該被替換(分解)爲多個臨時變量,每個變量只承擔一個責任。
做法:1.在待分解臨時變量的聲明及第一次被賦值處修改其名稱(如果賦值語句是i=i+”xxx”,說明該臨時變量在收集結果,則不分解)2.新變量聲明爲final;3.以第二次賦值動作爲界,修改此前對該臨時變量的所有引用點,讓其引用新臨時變量;4.在第二次賦值處重新聲明原先臨時變量;5.編譯測試。
6.7 Remove Assignments to Parameters(移除對參數的賦值)
解釋:代碼對一個參數進行賦值,以一個臨時變量取代該參數的位置。
動機:對參數的賦值降低了代碼的清晰度,而且混用了按值傳遞與按引用傳遞。
做法:1.建立一個臨時變量,將待處理的參數賦值給它;2.以“對參數賦值”爲界,將氣候所有對此參數的引用點,全部替換爲“對此臨時變量的引用”;3.修改賦值語句,使其改爲對新建之臨時變量賦值;4.編譯測試。
6.8 Replace Method with Method Object(以函數對象取代函數)
解釋:你有一個大型函數,對其中局部變量的使用是你無法採用提取函數手法。——將這個函數放進一個單獨對象中,如此一來局部變量就成爲對象內的字段,然後你就可以在同一個對象中將這個大型函數分解爲多個小型函數。
動機:小型函數優美動人,只要將相對獨立的代碼從大型函數中提煉出來,就可以大大提高代碼的可讀性,但是局部變量的存在會增加函數的分解難度,有時候會發現根本無法拆解一個需要拆解的函數,這時候以函數對象取代函數手法就可以將所有局部變量都變成函數對象的字段,然後就可以使用提煉函數了。
做法:1.新建一個類,根據待處理函數的用途來命名這個類;2.在心累中建立一個final字段,保存原先大型函數所在的對象,將這個對象稱爲源對象。同時針對原函數每個臨時變量和每個參數,在新類中建立一個對應的字段保存之;3.在心累中建一個構造函數,接收源對象和原函數的所有參數作爲參數;4.在新類中建一個compute()函數;5.將原函數的代碼複製到compute()函數中。如果需要調用源對象的任何函數,請通過源對象字段調用;6.編譯;7.將舊函數的函數本體替換爲這樣的話:“創建上述新類的一個新對象,而後調用其中的compute()函數”。
6.9 Substitute Algorithm(替換算法)
解釋:想要把某個算法替換爲另一個更爲清晰的算法,將函數本體替換爲另一個算法。
動機:解決問題一般都有多種方法,如果發現做一件事可以有更清晰的方式,就應該用更清晰的方式來代替原來的方式。算法也是一樣,“重構”可以把一些較爲複雜的東西分解爲較爲簡單的小塊,有時候需要刪掉整個算法,用更爲簡單的算法取代原先的算法。
做法:1.準備好另一個(替換用的)算法,讓其通過編譯;2.針對現有的測試,執行上述的新算法,如果結果與原本的結果相同,重構結束;3.如果測試結果不同於原先,在測試和調試的過程中,以舊算法爲比較參考的標準。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章