答應我,別再寫上千行的類了好嗎

2022-02-04 12:40:50 字數 3569 閱讀 3177

最近在對已有專案進行擴充套件的時候,發現要改動的乙個類長766行,開放了近40個public介面,我流著淚把它給改完了。為了防止這樣的慘劇再次發生在我的身上,我覺得有必要寫一篇部落格來讓廣大程式猿同胞知道**重構的重要性

如果你身邊有乙個類寫上千行的猿,一定要把此文轉給ta

此時cv工程師打了個冷戰

冗餘**,就是重複**,通常出自使用ctrl+c,ctrl+v來生產**的cv工程師之手,冗餘**的危害非常大:

冗餘**使方法、類過長,不簡潔

冗餘**會造成發散式修改(冗餘**需要變動時,每一處ctrl+v都需要修改)

乙個類開放幾十個介面,絕對存在職責過多的問題,就像圖中的tom貓一樣手忙腳亂,乙個類的職責過多也有巨大問題:

違反設計原則——單一職責原則(單一職責原則要求乙個類只實現乙個職責,比如乙隻tom只做掃地、擦桌、拖地中的一件事,而其他事的實現可以轉移給史派克狗或肥胖女傭),違反了這個原則會導致發散式變化、發散式修改、類過長等**問題,還會讓你的類難以擴充套件,甚至會讓其他程式猿認為你不專業

發散式變化(指引發此類修改的地方很多),如果乙個類的職責很多,那它的扇入(呼叫者)一定很多,每個呼叫者的修改都有可能讓你這個類不得不隨之修改,也就是發散式變化

就是說不管哪兒出了問題,你這個類都得遭殃

發散式修改(指此類修改引發修改的地方很多),相同的,如果乙個類職責很多,那支撐它實現的下級,即扇出(被呼叫方)一定很多,如果此類邏輯發生變動,所有下級被呼叫者可能都得隨之修改,也就是發散式修改

就是說你這個類出了問題,不管哪兒都會遭殃

難以擴充套件:如果你的乙個類介面非常多,那它的子類怎麼辦?它的包裝類怎麼辦?難道全部都要實現這麼多介面,全部都要承擔同樣多的職責嗎?擴充套件起來真的非常麻煩

觸發機關:【測試之怒】【運維之怒】

抽取冗餘**就是將重複**抽取成乙個獨立的方法,之後再使用這段**時就不再需要ctrl + c,ctrl + v,而是直接呼叫對應的方法即可

這樣做也可以縮短原方法,使原方法更加簡潔易懂

更值得一提的是如果這段**需要修改,也只需修改一處,而不是發散式地到處修改

真是一箭三鵰

使用idea進行冗餘**的抽取

找到重複**

進行方法抽取 右鍵->選擇重構->抽取->方法 (或者直接使用快捷鍵ctrl + alt + m)

自動檢測出個別重複**的細微差別,有些**可能只改動一兩個變數,idea會自動檢測出來,並在抽取方法時提醒我們,選擇左側accept signature change(接受簽名變動)可以使抽取的方法自動替換更多的重複點

可以選擇替換掉所有的重複**(竟然有18處)

重構——更改方法簽名

如果你對抽取出的方法的名字、引數、返回值或是修飾符不滿意,不要使用ctrl + r 修改,idea提供了重構方法——更改簽名(快捷鍵ctrl + f6)

注意:方法的名字指的是方法做了什麼,而非怎麼去做,最好是動詞+名詞格式

比如:tom.掃地() √

​ tom.掃地with掃把() ×

​ tom.用掃把掃地() ×

將不應該由自己管理的成員變數和函式轉移出去

那就要考慮兩個問題:該轉移誰?轉移給誰?

來看乙個圖

圖中成員【偏a】被類【a】呼叫兩次,而只被它所在的類【過長類】呼叫1次,因而應該轉移給【a】去管理

由於函式【偏a】與成員【偏a】的親密度較高(只呼叫了【偏a】),因而應與【偏a】共進退,同去留,轉移給【a】

成員【偏b】和函式【偏b】也是相同道理

職責1(函式和成員【偏職責1】)和職責2(函式和成員【偏職責2】)由於找不到可轉移的合適的類,所以應抽取出乙個新的類

注意,先決定移動哪個成員變數,然後再決定移動哪個函式

使用idea轉移成員變數和函式

移動成員變數,滑鼠選擇成員變數->右鍵->refactor->move,然後選擇轉移至哪個類

移動函式(與移動成員變數步驟相同)

當你發現要轉移的成員變數和函式找不到合適的類時**移職責卻找不到下家),要想起來,這裡是程式世界,而我們程式猿就是類和物件的造物主,是時候建立乙個新的類,讓它來替我們分擔職責(成員變數和函式)了

使用idea抽取類

重構 選中要搬的成員變數和函式,右鍵->refactor->extract->delegate(抽取乙個委託者,委託他來管理這部分變數和函式,如果只有變數或只有函式,可以抽出引數物件paramater object或方法物件method object)

不推薦抽取引數物件,因為一般引數物件是給引數多的方法用的(用引數物件取代一長溜的引數),而且如果成員變數抽取了也不會影響任何函式的話,那就是無用物件了,不如直接把他們刪除掉

為新類起個名,選個包吧

注意,抽取的函式和成員一定要符合乙個原則,那就是被抽取函式使用被抽取成員的次數一定高於剩餘函式的次數,不然違反親密性原則(成員應歸於呼叫它最多的類,沒有理由你用的比我多還讓我來管理)

一些小問題

由於抽取的函式直接使用了未抽取的物件而導致重構失敗,涉及到另乙個重構(使用get方法而非直接使用私有成員變數),使用此重構即可解決

技術不分領域,思想一脈相承

求求你們了,別再寫滿屏的 if else 了!

程式設計師想必都經歷過這樣的場景 剛開始自己寫的 很簡潔,邏輯清晰,函式精簡,沒有乙個 if else,可隨著 邏輯不斷完善和業務的瞬息萬變 比如需要對入參進行型別和值進行判斷 這裡要判斷下物件是否為 null 不同型別執行不同的流程。落地到具體實現只能不停地加 if else 來處理,漸漸地,變得...

拜託,面試別再讓我數1了!!!

面試中,除了topk,是否被問過 求乙個正整數的二進位制表示包含多少個1?畫外音 姊妹篇 拜託,面試別再問我topk了!例如 uint32 t i 58585858 i的二進位制表示 是 0000 00110 111 11 01 1111 0011 0000 0010 於是,i的二進位制表示包含15...

學妹一反常態主動聯絡我,我要不要答應幫她?

這麼久沒聯絡了,一上來就讓我幫忙?這 拍的,手抖的像是得了帕金森似的,字都有重影。放大 仔細看,這應該是某大廠的筆試題吧。這些題都不是很難,答案脫口而出。封裝封裝是把客觀事物封裝成抽象的類,並且類可以把自己的資料和方法只讓可信的類或者物件操作,對不可信的進行資訊隱藏。繼承繼承是讓某個類獲得另乙個類的...