识别Java中的代码气味
作為軟件開(kāi)發(fā)人員,我們不僅要編寫(xiě)有效的代碼,而且還要編寫(xiě)可維護(hù)的代碼,這是我們的責(zé)任。 Martin Fowler在他的《重構(gòu):改進(jìn)現(xiàn)有代碼的設(shè)計(jì)》中將代碼氣味定義為:
通常對(duì)應(yīng)于系統(tǒng)中更深層問(wèn)題的表面指示
重構(gòu)是在不影響代碼外部行為的情況下改進(jìn)代碼內(nèi)部結(jié)構(gòu)的過(guò)程。 理想情況下,我們應(yīng)該在添加新功能的同時(shí)重構(gòu)舊代碼。 與嘗試一次完成所有操作相比,這將節(jié)省我們一些時(shí)間。
福勒的書(shū)是極好的資源,可以幫助我們識(shí)別并消除一些常見(jiàn)的代碼異味。 在編寫(xiě)代碼以滿足新要求時(shí),我們還應(yīng)該避免這些代碼的味道。
在本教程中,我們將探索其中的一些。
1.評(píng)論:
理想情況下,我們應(yīng)該編寫(xiě)能說(shuō)明一切的代碼。 進(jìn)行大量評(píng)論被認(rèn)為是不好的做法。 當(dāng)我們使用大量評(píng)論時(shí),它們通常會(huì)隨著時(shí)間的推移而失去同步。 對(duì)于有時(shí)設(shè)計(jì)不當(dāng)?shù)南到y(tǒng),它們有時(shí)還會(huì)起到除臭劑的作用。
如果我們有一個(gè)好的設(shè)計(jì)并且正確地命名了我們的類,方法和變量,那么代碼將輕松地將其目的傳達(dá)給另一個(gè)開(kāi)發(fā)人員。
一些開(kāi)發(fā)人員喜歡在他們創(chuàng)建的新類上簽名自己的名字。 我個(gè)人不建議這樣做,因?yàn)槭褂萌魏伟姹究刂葡到y(tǒng)都可以輕松地跟蹤作者。
注釋在某些情況下可能會(huì)有用,但請(qǐng)謹(jǐn)慎使用。
2.重復(fù)的代碼:
復(fù)制代碼是當(dāng)我們?cè)诖a庫(kù)的多個(gè)位置分布相似的代碼時(shí)看到的代碼味道。 這是一個(gè)結(jié)構(gòu)不良的代碼,我們應(yīng)該找到某種方法以單獨(dú)的方法提取出通用功能。
重復(fù)代碼的問(wèn)題在于,如果要進(jìn)行更改,則必須修改所有這些文件以適應(yīng)該更改。 我們有可能錯(cuò)過(guò)一些代碼塊中的更新。
讓我們盡一切可能堅(jiān)持DRY(不要重復(fù)自己)原則。 根據(jù)DRY原則,我們不應(yīng)該重寫(xiě)已經(jīng)編寫(xiě)的功能。
3.長(zhǎng)方法:
我們應(yīng)該避免使用長(zhǎng)方法,這是一種不好的代碼味道。 太長(zhǎng)的方法很難閱讀,并且很難適應(yīng)它的新變化。 開(kāi)發(fā)人員通常會(huì)爭(zhēng)論多長(zhǎng)時(shí)間。 就我個(gè)人而言,我更喜歡堅(jiān)持一種方法大小的規(guī)則,該規(guī)則不應(yīng)超過(guò)十五行代碼。 在大多數(shù)情況下,此規(guī)則對(duì)我來(lái)說(shuō)非常有效。
每當(dāng)我看到自己違反此規(guī)則時(shí),就會(huì)問(wèn)自己: “此方法僅做一件事情(SRP原理)嗎?” 。 如果不是,那么我嘗試在邏輯上將我的方法拆分為更有意義的方法。
盡管使用長(zhǎng)方法有時(shí)會(huì)很好,但是約束是我們應(yīng)該有足夠的理由來(lái)證明它合理。
4.大班:
不出所料,我們列表中的下一個(gè)是大型類代碼氣味。 大型課程通常也稱為“上帝課程”或“斑點(diǎn)或黑洞課程”。
在大型系統(tǒng)中,我們經(jīng)常會(huì)遇到這種代碼異味。 隨著系統(tǒng)的發(fā)展,某些類最終會(huì)在一段時(shí)間內(nèi)支持添加到其中的許多功能。 盡早捕獲此代碼氣味是個(gè)好主意。 如果一個(gè)類變得太大,則稍后將花費(fèi)大量時(shí)間和精力進(jìn)行修復(fù)。
根據(jù)“ 單一職責(zé)原則”(SRP),類必須做一件事并且做到這一點(diǎn)。 在向現(xiàn)有類中添加一些代碼時(shí),讓我們利用開(kāi)發(fā)人員的直覺(jué)并自問(wèn): “此類是否真的應(yīng)該支持此功能?”。 如果沒(méi)有,最好將其放置在其他位置。
5.長(zhǎng)參數(shù)列表:
另一個(gè)類似的代碼味道是長(zhǎng)參數(shù)列表。 長(zhǎng)參數(shù)列表的方法可能難以使用,并且由于疏忽而增加了錯(cuò)誤映射的可能性:
public void doSomething(String name, int id, String deptCode, String regNumber) { ?... }此處的解決方案是引入捕獲上下文的參數(shù)對(duì)象。 因此,我們可以將上述方法改進(jìn)為:
public void doSomething(Student student) { ... }在這里,我們已經(jīng)實(shí)現(xiàn)了正確的封裝。
6.數(shù)據(jù)類別:
數(shù)據(jù)類是僅包含數(shù)據(jù)成員及其獲取器和設(shè)置器的類:
public class Student { ?private int id; private String name; ?//constructor, getters and setters ? }這通常表明它可能不是一個(gè)很好的抽象。
盡管我們創(chuàng)建參數(shù)對(duì)象來(lái)解決“長(zhǎng)參數(shù)”代碼的味道, 但理想情況下 , 我們應(yīng)該設(shè)計(jì)類,其功能不僅僅是存儲(chǔ)數(shù)據(jù)。
我們應(yīng)該問(wèn)類似的問(wèn)題: “我可以向當(dāng)前在其他地方處理的此類添加一些功能嗎?”
有時(shí),當(dāng)我們?cè)诖a庫(kù)中的多個(gè)地方處理了這些數(shù)據(jù)類的功能時(shí),我們會(huì)意識(shí)到會(huì)出現(xiàn)重復(fù)的代碼味道。
7.發(fā)散類:
當(dāng)我們意識(shí)到出于多種不同原因必須以多種不同方式更改類時(shí),會(huì)產(chǎn)生不同的類代碼氣味。
正如我們前面討論的,類應(yīng)僅具有一個(gè)特定的目的。 如果是這樣,我們將有較少的理由要對(duì)類進(jìn)行更改,并且要在其中進(jìn)行的更改也較少。
如果我們發(fā)現(xiàn)自己以多種方式更改了一個(gè)類,那么這很好地表明該類的職責(zé)需要分解為單獨(dú)的類。
8.消息鏈:
消息鏈?zhǔn)且环N代碼味道,我們?cè)趯?duì)象上調(diào)用一個(gè)方法,然后在該返回的對(duì)象上調(diào)用另一個(gè)方法,依此類推:
int id = obj.getDept().getSubDept().getHOD().getId();較長(zhǎng)的消息鏈?zhǔn)刮覀兊南到y(tǒng)變得僵硬,難以獨(dú)立測(cè)試。
它通常也違反Demeter法則,該法則規(guī)定了良好的面向?qū)ο笤O(shè)計(jì)應(yīng)允許調(diào)用哪些方法。
9. Shot彈槍手術(shù):
realize彈槍手術(shù)是一種代碼氣味, 當(dāng)我們意識(shí)到我們必須觸摸許多類以針對(duì)一個(gè)簡(jiǎn)單的要求進(jìn)行更改時(shí) , 就會(huì)發(fā)生這種情況。 當(dāng)觸及我們代碼庫(kù)中的許多地方時(shí),很可能會(huì)引入錯(cuò)誤并破壞現(xiàn)有的實(shí)現(xiàn)。
對(duì)于設(shè)計(jì)良好的系統(tǒng),較小的更改將理想地要求將本地更改更改為一兩個(gè)位置。 雖然,這很難實(shí)現(xiàn),但是無(wú)論我們對(duì)代碼的設(shè)計(jì)水平如何,有時(shí)都需要進(jìn)行shot彈槍手術(shù)。
我們可以通過(guò)移動(dòng)方法來(lái)解決the彈槍外科手術(shù)代碼的氣味。 如果更改要求我們修改多個(gè)類的方法,我們應(yīng)該問(wèn)自己: “這些方法應(yīng)該合并為一兩個(gè)類嗎?” 然后讓我們的開(kāi)發(fā)人員本能引導(dǎo)我們。
10.功能嫉妒:
功能嫉妒是一種代碼氣味,當(dāng)我們擁有的方法對(duì)其他類的詳細(xì)信息比對(duì)其中的類更感興趣時(shí),就會(huì)發(fā)生這種情況。
如果兩個(gè)或多個(gè)方法總是互相交談,則它們很可能必須屬于同一類。
11.不適當(dāng)?shù)挠H密關(guān)系:
當(dāng)兩個(gè)類通過(guò)雙向通信彼此之間過(guò)于依賴時(shí),這是不適當(dāng)?shù)挠H密代碼氣味。
在班級(jí)之間進(jìn)行雙向交流使他們緊密地聯(lián)系在一起。 我們至少應(yīng)該將某些方法分解為一個(gè)單獨(dú)的類,并以消除這種循環(huán)為目標(biāo)。 我們應(yīng)該設(shè)計(jì)易于理解和維護(hù)的類。
12.原始癡迷:
顧名思義,有時(shí)我們過(guò)于依賴原始類型。 盡管我們?cè)诖a中需要基元,但它們應(yīng)存在于代碼的最低級(jí)別。
我們應(yīng)該避免過(guò)度使用原語(yǔ),并在需要時(shí)定義合適的類。
13.投機(jī)性:
有時(shí), 我們過(guò)度設(shè)計(jì)諸如定義超類或某些當(dāng)前不需要的代碼之類的東西,但我們認(rèn)為有一天可能會(huì)有用。 此代碼氣味被稱為推測(cè)性普遍性。
敏捷開(kāi)發(fā)促進(jìn)采用準(zhǔn)時(shí)設(shè)計(jì)。 我們的設(shè)計(jì)應(yīng)該保持簡(jiǎn)單,并且應(yīng)該足以支持當(dāng)前的功能。 用戶需求經(jīng)常快速變化,因此,僅在必要時(shí)才應(yīng)引入概括。 否則,我們可能最終將時(shí)間浪費(fèi)在最終從未使用過(guò)的設(shè)計(jì)上。
14.拒絕的請(qǐng)求:
當(dāng)子類繼承某些東西但不需要它時(shí),就會(huì)出現(xiàn)拒絕請(qǐng)求代碼的氣味。
如果子類繼承了它們不使用的東西,則它們可能不是超類的適當(dāng)子類:
public class Bird { ?void fly() { System.out.println( "Flying!!" ); } } ? public class Ostrich extends Bird { ?void fly() { throw new IllegalStateException( "An ostrich can't fly" ); } }顯然,鴕鳥(niǎo)不會(huì)飛行,因此這是拒絕請(qǐng)求代碼氣味的一個(gè)示例。 我們可以通過(guò)以下方式之一來(lái)處理此代碼異味:
- 要么不要在超類中定義不需要的行為,要么
- 將它們創(chuàng)建為單獨(dú)的獨(dú)立類
結(jié)論:
在本教程中,我們研究了一些代碼氣味,并學(xué)習(xí)了如何避免和處理它們。
該列表顯然并不詳盡,但可以證明是快速入門指南。
翻譯自: https://www.javacodegeeks.com/2019/09/identifying-code-smells-in-java.html
總結(jié)
以上是生活随笔為你收集整理的识别Java中的代码气味的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 电力工人如何抢修设备电力工人如何抢修设备
- 下一篇: java 编译 器 ide_在没有IDE