作者:翻滾吧李博
連結:https://www.jianshu.com/p/7e8bd46d37ba
核心思想:拆細 公用
重構可以是修改變數名、重新安排目錄這樣簡單的物理重構,也可以是抽取子函式、精簡冗餘設計這樣稍許複雜的邏輯重構。但均不改變現有程式碼的功能。
瞭解敵人——醜陋的程式碼
- 臃腫的類
開發者缺乏對最基本的編碼原則,即“單一職責原則”(SRP)的理解。開發者不去思考這些功能是不是應該放在這同一個類中,導致這些類會變得很臃腫,造成一個類幾千行,讓下一個接盤俠欲哭無淚。 - 臃腫的方法
好幾十上百行的一個函式堆在一塊,用面向過程的思想來寫程式碼。 - 函式引數過多
函式引數過多會導致呼叫者對方法難以理解,引數弄混。想象一下一個函式連續傳5個int值引數,能分清誰是誰嗎?建議可以將引陣列成一個物件傳入。 - 層層巢狀的判斷
如果邏輯不複雜儘量減少if-else的分支包裹,他人太難閱讀。比如不滿足條件了直接return,不走其他程式碼,這樣可以減少一層巢狀。 - 滿篇跑的常量值
一個類裡面出現各種未命名的常量值。0,1,200等等鋪天蓋地。這種狀態碼意義改了,改程式碼會把你改哭的。難道就不能先宣告一個統一的常量變數來使用嗎。 - 模稜兩可的命名
不能根據名字一眼看懂它的功能的命名不是一個好命名。當然生僻的單詞除外。模糊的,沒有功能意義的命名會給閱讀造成很大困難。
重構之道
- 分拆大函式: Break Method
當函式比較大了,就可以根據功能節點分拆成多個小函式,也許其中的小函式還可以公用。比如結算購物車,包括計算各類商品的總價,再計算折扣,再計算滿減優惠,如果一個方法執行完,那麼別人要只要邏輯就要從頭到尾讀一遍。而分別拆分成三個,一眼就能看出這段邏輯先後做了什麼。寫方法切忌一口吃一個胖子。 - 封裝到父類:
如果多各類要執行相似的功能和程式碼,可以把該方法放到它們的父類中,或者提取出來成業務工具類。 - Move Method—-方法遷移
遵守“單一職責”原則,當類中的方法不適合放在當前類中時,就應該為該方法尋找合適下家。移到與方法耦合大的類中。當一個方法被其他類使用比在它所在類中的使用還要頻繁時,我們就需要使用遷移方法重構了——將方法遷移到更頻繁地使用它的類中。 - Move Field—-搬移欄位
當在一個類中的某一個欄位,被另一個類的物件頻繁使用時,我們就應該考慮將這個欄位的位置進行更改了 - Extract Class—-提煉類
一個類如果過於複雜,做了好多的事情,違背了“單一職責”的原則,所以需要將其可以獨立的模組進行拆分,當然有可能由一個類拆分出多個類。
對類的細化也是為了減少程式碼的重覆性,以及提高程式碼的復用性,便於程式碼的維護。 - 提升方法、欄位(Pull Up Method)
將方法向繼承鏈上層遷移的過程。用於一個方法被多個實現者使用時。在繼承的體系中,當多個類使用了相同或類似的方法,就可以考慮將該方法抽取到基類,沒有基類就建立一個。欄位提升同方法。 - 降低方法
即父類抽象方法讓多個子類實現。多個子類有相同的功能但是有各個具體的實現方法,那麼這種封裝就可以用多型性了,父類建立一個抽象方法,將方法實現降低到子類。 - 重覆程式碼的提煉
有時候為了趕專案進度,儘快完成功能,會偷懶將實現功能的一片程式碼複製一遍,直接套用。這種把多餘的刪掉,保留一個,也許只需傳一兩個引數就可以封成一個方法供多處呼叫。 - 重新命名變數(類、方法、變數)
這個很重要,可以不誇張地說,命名的水平就體現了程式設計能力的高低。在重構的過程中,當發現類名,方法名在當前版本不符合它的功能含義,就該考慮對其重新命名。 - 補加註釋
對於全域性變數,公用函式,邏輯複雜的地方新增註釋,彌補之前的遺漏。 - 將較長的判斷或程式碼運算用臨時變臉暫存
if(stateCode = OK && datas != null && canShow)
function(Math.random((num1-num2)*num3))
如上這種長長的判斷條件和引數會使 這種程式碼應該先將if判斷條件寫成一個變數,放入變數判斷,將function引數寫一個區域性變數儲存結果,再傳入方法。
- 使用泛型封裝成統一的方法或類
- 函式要避免過多的引數造成閱讀的複雜性
public void requestPhoneThirdRegister(String loginway, String nickname, String openId, String token, String expires, String phone, final CallBackimpl callBackimpl)
用這樣的方法直接傳引數就太長了,嚴重降低程式碼可讀性。我們可以將引數變數寫到一個物體類中,透過構造方法初始化物件屬性值,只需要傳遞一個物件就搞定,也解決了增減引數帶來的變動問題。
- 巢狀條件分支最佳化
if(){
if(){
if(){
}
}
}else{
}
相信大家也見識過不少這樣的箭頭程式碼,像怎麼也解不開的死結。遇到這種程式碼,一定要盡可能要最佳化。通常做法:判斷陳述句,if條件成立,執行程式碼塊,誒,這樣就生成了一個巢狀層級。
最佳化的核心思想:直接判斷不滿足的條件,if條件成立,直接return,儘快跳出方法來減少巢狀的層級。
第二種:將條件判讀合併
- 儘量避免雙重否定的條件
private boolean isChecked(){
if(){
return true;
}
}
一個條件方法。
if(!isChecked()){
}
然後用否定來判斷這個條件,這樣可能會一時之間轉變不過來導致條件判斷反了。當然頭腦靈活的忽略這條。
- 去除東北亂燉的Util類
當我們在寫程式碼中偶然間需要抽出公用方法時,一時之間找不到合適的類去放置,然後就隨意地放進了XXUtil或XXManager類中。長此以往,該類所含功能越來越雜,dp和px轉化在其中,螢幕尺寸相關方法在其中,日期轉化在其中,加密的索性也放在其中,那有無網路,網路型別判斷也加入吧。這不就像垃圾場了嗎,各類雜物都堆在其中,不符合單一職責原則,應該按照如上的功能塊分解成多個職責單一的類。類多不要緊,關鍵要做到職責單一。 - 將滿篇跑的魔鬼數字和字串用定義的常量表示。
如果只是某個類或者某個模組需要用到該常量,就宣告到對應類中。如果是全域性專案都會用到的常量,就提升到專案的常量配置檔案中。
●編號381,輸入編號直達本文
●輸入m獲取到文章目錄
Java程式設計
更多推薦《25個技術類公眾微信》
涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。