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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

重构,体现一个工程师的基本素养和底蕴(细节篇)

發布時間:2024/7/23 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 重构,体现一个工程师的基本素养和底蕴(细节篇) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

重構小記(重構,改善既有代碼的設計讀后總結)


方法級別

提煉函數:

將一個大方法,拆成多個小方法,難點在于小方法的命名。
假如有早上上學的一個大方法,
那么就應該在里面有起床,穿衣服,吃早點等小方法,
而起床這個方法又可以分為穿衣服,穿鞋,疊被子等方法,
而穿衣服又有穿內衣,穿外衣等步驟。

內聯函數

一個太小的方法,里面只做了一個動作,就可以考慮去掉了,去掉會使代碼更清晰。
感覺內聯函數說的就是不要過度的提煉函數。

內聯臨時變量

就是說要盡量去掉只用一次的臨時變量
注意:如何判斷臨時變量只被賦值了一次,就是在前面加flnal,能編譯過就是只有一次
比如有代碼:int age = user.getAge();return age>18;
那么這兩句代碼,就完全可以合成一句:return user.getAge()>18;

以查詢取代臨時變量

如果臨時變量被用了多次的話,也盡量用方法去代替臨時變量。
比如有代碼:
int sum = a + b;
if(sum>10){
return sum2;
}
else{
return sum3;
}
這里的sum變量,就要用方法封裝后去掉。變成以下:
int getSum(){
return a + b;
}
if(getSum()>10){
return getSum()*2;
}
else{
return getSum()*3;
}
這種不一定就是最正確的,甚至有時候會反過來改,所以把握整體很關鍵。
這樣可能會對性能產生一點影響,但幾乎也可以忽略不計。

引入解釋型變量

用變量名來簡化復雜表達式
比如有代碼:if(a > 0 && aa > 0 && aaa> 0 && b < 0 && bb < 0 && bbb < 0){return true;}
這個if中的判斷表達式就太復雜,可改成下面這樣:
boolean aClassMoreThanZero = a > 0 && aa > 0 && aaa> 0;//a類變量大于0
boolean bClassLessThanZero = b < 0 && bb < 0 && bbb < 0;//b類變量小于0
if(aClassMoreThanZero && bClassLessThanZero){return true;}

分解臨時變量

臨時變量被賦值多次,并且是不同的意思,就要將臨時變量分解成多個變量,保證一個臨時變量只代表一個意思。

移除對參數的賦值

一個方法中,盡量不要對入參進行賦值,可以嘗試將入參用final修飾

以函數對象取代函數

如果方法中局部變量太多,就創建一個函數對象,把所有的局部變量變成這個對象的屬性,然后把這個方法的實現全部移到對象中
說實話,我個人覺得這種方式沒什么用,因為如果你有時間這樣搞,還不如換個思路梳理下這個方法,不過也可能是我沒遇到這樣情況。

替換算法

替換一個方法的實現,重點在于替換前后要保證執行結果一致。
比如有個方法,是冒泡算法實現排序,那么就可以用插入排序來替代冒泡排序。


對象級別

搬移函數

一個類中有個函數,卻與另一個類進行更多的交流,那么就嘗試將這個函數整體遷移過去。

搬移字段

一個類中有某個字段,被另一個類更多的用到,那么就嘗試將這個字段移到另一個類中。

提煉類

如果一個類越來越大,就可以考慮將其中的一部分與類名關系不大的屬性抽出來,做成這個類的子類。

將類內聯化

與提煉類相反,如果一個類沒有做太多事情,就可以嘗試將他與另一個類合并。

隱藏委托關系

隱藏真實的調用關系,將真實的調用關系封裝在最外層的方法中。
比如有個A類,A中有個B類屬性,B中又有個C類。
如果想通過A獲得C的值,普通的做法就是a.getB().getC();但是這樣就把真實的C暴露出來了,其實調用方也根本沒必要知道這個,這時候就可以這樣做:
在A類中添加一個方法,叫getC(),方法的實現就是return b.getC();這樣就相關與把B給隱藏了;
這時如果再想通過A獲得C,那么就可以直接a.getC();這就是隱藏委托關系。

移除中間人

這個是隱藏委托關系的反向重構,也就是說當委托的太多的時候,就應該考慮是不是應該直接點,物極必反。
相當于把上個例子的a.getC()又改成a.getB().getC();

引入外加函數

對于有些引入的源碼類,如果你想增強它,肯定不能直接修改源碼,那么就需要引入外加函數。
感覺這個就是對源碼的增強,通過新建一個函數,返回增強后的內容,以后使用的時候就直接用這個函數就好了。

引入本地擴展

這個是引入外加函數的增強版
如果需要對源碼進行很多增強,那么就可以這樣做
方案一:新建一個類(子類),繼承源碼類,新增強的功能就可以放在子類中
方案二:新建一個類(包裝類),將源碼類當成這個新類的屬性,那么也可以完成和方案一一樣的效果


重新組織數據

自封裝字段

屬性私有化,通過get/set方法取值/設置值,而不是直接對值進行操作

以對象取代數據值

屬性使用對象,這是一個過程,比如剛開始是String,后面慢慢的感覺還要一些字段,而這些字段又是一類數據,那么就將這一類數據抽成一個類來代替原來分散的數據。

將值對象改為引用對象

將一個類衍生出的彼此相同的許多實例,使用工廠替換成同一個實例,并將原類的構造方法私有化,禁止直接構造。
比如員工是個類,類中有個屬性是老板這個類,那么多個員工的老板,其實就是同一個,這時候就要用這種方式,先將老板類的構造方法私有化。
然后可以先在靜態塊中將老板類創建好,然后每new一個新員工的時候,老板類的賦值就用這個創建好的老板實例就行。

將引用對象改為值對象

如果引用對象太小,且不可變,難以管理,就可以考慮將上面的方式反過來了

以對象取代數組

如果數組中有多個不同的類型,復雜到不能讓人很快的明白哪個是什么意思,就該用對象來代替了
比如數組=[“小明”,20,175,70];不是很容易就看出來這個數組中每個的意思,這時就要用對象代替,那么屬性就分別是名字,年齡,身高,體重。

以字面常量取代魔法值

任何一個數字,都不應該憑空出現,需要在類前將它設置成常量,然后再去引用,這樣才能使代碼變的清晰易懂。

以類取代類型碼

1、2、3、4等類型碼,不能很清楚的表達意思,這時候就應該用類來代替這些類型碼。
Lei.A.getCode(),代替原來的1;
Lei.B.getCode(),代替原來的2;…

以子類取代類型碼

類中多個類型碼,可以使用子類,每個子類對應一個類型碼。
使用的時候,根據不同的條件,原來是返回不同的類型碼,現在改為返回不同的子類。

以state/Strategy取代類型碼

如果類由于其他原因,不能被繼承,那么就需要新建一個狀態類,然后不同的子類去繼承這個狀態類,還是一個狀態對應一個子類。
然后這個狀態類中建立一個查詢方法,返回狀態碼,各個子類重寫這個方法,返回具體的狀態碼。

以字段代替子類

如果子類差別不大,甚至只是在返回常量數據不一樣,那么就要考慮刪掉子類了,原子類的功能,完全可以通過不同的方法來代替。


簡化條件表達式

分解條件表達式

表達式太多,就會分不清每個的意思,這時候就應該把表達式按照意思區分成小塊,每個小塊通過命名來區分具體的意思。

合并條件表達式

如果多個表達式,返回同一個結果,就要考慮合并成一個表達式。

合并重復的條件片段

如果每個表達式的分支上都有著相同的代碼塊,那么這就是重復代碼,我們就應該把這塊代碼提煉出來。

移除控制標記

就是類似于if(怎么樣)就停止 這樣類似的控制標記,可以用break,continue,return等替代,多用在循環體中。

以衛語句取代嵌套的條件表達式

盡量避免嵌套的條件表達式
比如if(){}else{if(){}else{if(){}}}
盡量改成if(){};if(){};if(){};…

以多態取代條件表達式

將條件表達式的每個分支放在每個子類重寫的方法中,原始方法變抽象
多態就是根據對象的不同類型采取不同的行為。

引入斷言

斷言是一個條件表達式,應該總是為真,如果失敗,則說明程序員犯了錯誤。
Assert類有很多方法,常用的是Assert.isTrue(XXX);
斷言不會改變程序任何行為,但能幫助程序員理解代碼正確運行的必要條件。


簡化函數的調用

函數改名

函數的名字至關重要,代碼首先是為人寫的,其次才是為機器寫的

添加參數

添加參數要謹慎,實在沒辦法的情況下,才會考慮添加參數。

去除參數

如果某個參數確實沒用了,記得趕緊去掉,不要抱著不去也不會出問題的心態,否則這樣只會讓程序越來越復雜。
去除參數的時候,還要注意多態的影響。

將查詢函數和修改函數分離

一個函數只完成一個職責,要么查詢,要么修改。

另函數攜帶參數

統一合并類似功能的函數
如果多個函數只是部分值不一樣,做的確實一類事情,那么就要考慮合并成一個函數,并把不同的值當成參數傳進來。

以明確函數去掉參數

與上面的相反,如果一個函數中根據入參不同,執行的邏輯已經完全不同了,那么就將他們分開成各自的函數。

保持對象完整性

如果入參是某個對象的多個屬性時,考慮用這個對象整體作為入參,來代替多個參數。

以函數取代參數

如果函數的參數可以通過函數內部通過其他方法得到,就不要使用參數來傳遞。
盡量的簡化函數的參數,因為太多的參數會讓函數變的復雜。

引入參數對象

如果一些參數總是很自然的同時出現,那么就要考慮是不是用一個對象來代替這些參數了

移除設值函數

如果一個字段在函數過程中需要被修改,就為它提供一個修改的函數
如果這個字段在函數過程中不會被修改,就刪掉修改它的函數,同時用final修飾。

隱藏函數

一個函數從沒有被其他類用到,就用private修飾它

以工廠函數取代構造函數

如果創建對象時,需要根據不同的條件創建不同的對象,可以考慮寫個工廠方法來代替構造方法

封裝向下轉型

函數的返回值,不要設置成Object等原始類型,最好指定類型,免得調用方又要強制轉型。

以異常取代錯誤碼

就是自定義異常,并合理的利用它

以測試取代異常

就是方法一開始,要對入參進行校驗,看是否是后面程序可以接受的參數
比如如果為空,要怎么樣,如果為0,要怎么樣,這些能想到的要提前抓到并做合理的處理。最常見的就是判空。


處理概括關系(繼承關系)

字段上移

子類有相同作用的字段,應該上移到他們的父類中。

字段下移

與上面相反,如果父類的某些字段,只與部分某些子類有關,那么就應該將這些字段下移到對應的子類中。

函數上移

子類中有相同作用的函數,應該將這個函數上移到他們的父類中。

函數下移

與上面相反,如果父類的某些函數,只與部分某些子類有關,那么就應該將這些函數下移到對應的子類中。
####構造函數本體上移
各個子類中的一些構造函數,如果他們的本體幾乎完全一致,那么就在父類中建一個構造函數,然后在子類中調父類,類似于super(id,name)

提煉子類

如果類中的某些屬性,只被某些實例用到,那么就新建個子類,將這些屬性移到子類中。
這樣保證不用這些屬性的實例,用父類new;用到這些屬性的實例,用子類new。

提煉超類

如果兩個類有相似的特性,就新建一個他們的父類,將相同特性移到父類上去,避免代碼的重復。

提煉接口

若干類中有相同的子集,可以考慮新建接口讓這些類繼承,然后統一相同的部分,調用也改成接口調用

折疊繼承體系

如果子類和父類沒有太大區別,就將他們合為一體

塑造模板函數

詳見設計模式的模板模式,就是在抽象父類中定義好抽象函數,再寫個方法A,寫明各個抽象函數的調用順序。
那么子類中只需要重寫父類的抽象方法,用的時候,子類調用方法A,就能按照父類方法A中的調用順序來調用各自的實現了。

以委托取代繼承

如果父類中的很多屬性和方法,子類都用不到,那么就可以放棄繼承,在子類中引入一個原父類的屬性引用即可。

以繼承取代委托

與上面的相反,如果兩個類之間的委托調用的太多,甚至幾乎是全部了,那么就該考慮讓委托的這個類作為父類了,之間繼承會更好。


大型重構

梳理并分解繼承體系

某個繼承體系同時承擔兩項責任或多項責任時,考慮拆開。
比如原來有父類A,A有兩個子類B和C,B還有個子類D,C還有個子類E。B和C都承擔同類責任,而D和E卻承擔另外一個不同的責任。
此時就應該把BC和DE分開了。我們建立兩個繼承體系A和F,A擁有子類BC,F擁有子類DE,通過委托關系(A作為F的屬性)讓其中一個F調用另外一個A。

將過程化設計轉為對象設計

其實就是盡量的面向對象,將面向過程變成轉成面向對象編程。

將領域和表述/顯示分離

與界面展示相關的邏輯的類,和純業務邏輯的類,要分離。

提煉繼承體系

一個類在演變過程中,所承擔的責任越來越多時,就應該考慮分離它了,可以采取抽出一部分當成子類,使整體變成繼承的體系。

總結

以上是生活随笔為你收集整理的重构,体现一个工程师的基本素养和底蕴(细节篇)的全部內容,希望文章能夠幫你解決所遇到的問題。

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