講點碼德!避免這些代碼壞味道,努力做一名優秀的程式設計師

2020-12-05 51CTO

shenfq  本文轉載自微信公眾號「愛笑的架構師」,作者雷架。轉載本文請聯繫愛笑的架構師公眾號。 

Martin Fowler:任何一個傻瓜都能寫出計算機可以理解的代碼。唯有寫出人類容易理解的代碼,才是優秀的程式設計師。

大家閉著眼睛想一下什麼是好代碼?也許你的腦海中漂浮著一堆詞:乾淨、整潔、命名規範、注釋合理、高內聚低耦合……

人人都想寫好代碼,因為看好代碼就如同看一位五官端正的女子,心情愉悅、舒暢,而看糟糕的代碼就如同看見腐爛的食物,聞起來也有一股壞味道。

大多數人寫的代碼都不能稱之為好代碼,一方面由於自己技能限制,另一方面也可能根本就分不清好代碼和壞代碼,下面筆者結合日常編碼實踐與大家分享一下常見的代碼壞味道。

壞味道:Long Method(過長函數)

過長函數簡而言之就是函數長度超標了,包括橫向和縱向。

為什麼過長函數是一種壞味道?

橫向過長會導致無法一眼就能看出這行代碼的作用,需要用滑鼠慢慢往後邊拖,相信用小屏幕的小夥伴經常會遇到這個問題,拖動的過程會嚴重影響讀代碼的效率。

縱向過長其實就是出現了大函數,一個函數的行太多,使得函數難以讀懂,代碼修改難度大。

那麼如何解決過長函數問題呢?

關於橫向過長的問題,一般會在 IDE 中提前配置好最大寬度,比如80字符或者120字符(具體根據公司內部規範設置),然後格式化代碼即可解決。

比如我們在寫 Java8 stream 鏈式表達式的時候可以會很長:

  1. List<String> nodes = list.stream().filter().filter().map.filter().collect(Collectors.toList()); // 可能會非常長 

其實我們可以在.之前換行,這樣看起來一目了然。

  1. List<String> nodes = list.stream() 
  2.   .filter() 
  3.   .filter() 
  4.   .map 
  5.   .filter() 
  6.   .collect(Collectors.toList()); 

關於縱向過長的問題其實就是這個方法或者函數職責不夠單一,一個函數中堆積太多功能。

重構的手段很簡單:Extract Method,積極抽取函數或方法,隱藏細節保持職責單一。

壞味道:Large Class(過大的類)

過大的類也常常被成為上帝類(God Class),上帝類一般是指維護了太多功能(違反單一職責原則),連上帝也看不懂你的代碼。

知識小百科

設計模式的六大原則有:

Single Responsibility Principle:單一職責原則

Open Closed Principle:開閉原則

Liskov Substitution Principle:裡氏替換原則

Law of Demeter:迪米特法則

Interface Segregation Principle:接口隔離原則

Dependence Inversion Principle:依賴倒置原則

六個原則的首字母聯合起來就是 SOLID,兩個 L 當成一個。

那如何判斷一個類是不是上帝類呢?

一般一個類同時滿足以下3個條件就是上帝類:

(1)CPFD (Capsules Providing Foreign Data) 從多個不相關類(模塊)中引用數據。

(2)WOC (Weighted Operation Count) 類的所有函數的圈複雜度之和超過65。

(3)TCC (Tight Capsule Cohesion) TCC < 1/3 類需要具有低內聚的特性(類中直接相關的方法與全部方法之比小於1/3),也就是較少的private方法。

為什麼過大的類是一種壞味道?

過大的類承擔了過多的職責,往往有很多重複代碼,並且這些重複代碼你還不容易發現,這基本就是壞味道的開始。

過大的類被子類繼承會導致其他壞味道,比如遺留的饋贈。

如何解決過大的類這種問題呢?

通過觀察這個過大類的屬性,看有沒有一些屬性有關聯,如果有可以使用 Extract Class 將這些關聯屬性抽象到一個新類中,並將與這些屬性相關的操作都 Move 到新的類中。

通過觀察這個過大類的方法,看有沒有一些函數或方法存在兄弟關聯,如果有可以使用 Extract Subclass(提煉子類)的手段將這些方法提煉到子類中,子類可以繼承父類。將相似的行為方法聚集在一個類中拆分到多個類中,可以進一步將發放調用解耦開。

以上方法循環往復,一個大類就可以拆分為多個小的且職責單一的類。

壞味道:Duplicated Code(重複代碼)

Robert C.Martin:重複可能是軟體中一切邪惡的根源。

重複代碼一般是由於複製粘貼造成的。需求迭代過程中為了不影響已有功能,通常是將之前的代碼copy一份改改,然後匆匆上線。

那為什麼重複的代碼是一種壞味道呢?

最直接的弊端就是如果你想修改一段代碼邏輯,可能會遺漏,甚至需要多次修改才能確保全部修改完。

如何解決重複代碼的問題?

下面結合代碼實踐分幾個場景分別描述。

場景1:同一個類中兩個方法含有相同的表達式

  1. class A { 
  2.     public void method1() { 
  3.         logic1 
  4.         logic2 
  5.         logic3 
  6.     } 
  7.     public void method2() { 
  8.         logic1 
  9.         logic2 
  10.         logic4 
  11.     } 

重構手段:將兩個方法共同的邏輯抽象出來。重構後:

  1. class A { 
  2.     public void method1() { 
  3.         baseMethod(); 
  4.         logic3 
  5.     } 
  6.     public void method2() { 
  7.         baseMethod(); 
  8.         logic4 
  9.     } 
  10.     public void baseMethod() { 
  11.         logic1 
  12.         logic2 
  13.     } 

場景2:兩個具有相同父類的子類內含有相同的表達式類 A 中有一個 method1,有三段邏輯。

  1. class A extend Base { 
  2.     public void method1() { 
  3.         logic1 
  4.         logic2 
  5.         logic3 
  6.     } 

類 B 中有一個 method2,也有三段邏輯。

  1. class B extend Base { 
  2.     public void method2() { 
  3.         logic1 
  4.         logic2 
  5.         logic3 
  6.     } 

重構手段:將重複代碼抽象成一個方法放在父類中,差異部分由子類各自實現。

重構後:

  1. class Base { 
  2.     public void baseMethod() { 
  3.         logic1 
  4.         logic2 
  5.     } 
  6. class A extend Base { 
  7.     public void method1() { 
  8.         baseMethod(); 
  9.         logic3 
  10.     } 
  11. class B extend Base { 
  12.     public void method2() { 
  13.         baseMethod(); 
  14.         logic3 
  15.     } 

場景3:兩個毫無相關的類出現重複代碼

如果兩個沒有直接關聯的類出現重複代碼,可以考慮將重複的代碼抽象到一個獨立的普通類或者工具類中,適用方可以使用組合的方式調用。

代碼樣例這裡不再贅述,與場景1和2大同小異,相信聰明的你一定能明白。

壞味道:Long Parameter List(過長參數列)

全局變量是個邪惡的東西,數據是共享的並且每個線程都可以修改,太多了容易導致程序不可控,所以大家喜歡將變量以行參的方式傳遞,久而久之參數列越來越長了。

為什麼過長參數列是一種壞味道?

方法參數的數量太多會導致代碼可讀性非常差,如果有多個重載方法它們的方法參數都非常多,在寫代碼時很難判斷該調用哪一個。

當一個方法需要新增功能,每次都可能會新增一個方法參數,這樣導致調用方每次都要重新適配,小心被打哦,耗子尾汁。

如何解決過長參數列這種壞味道?

可以將多個參數封裝到一個 DTO 對象中,方法間的對象通過對象的傳輸而不是過長的參數。

數據傳輸對象(DTO)(Data Transfer Object),是一種設計模式之間傳輸數據的軟體應用系統。

特別需要提醒的是有些情況下長參數也是合理的,因為使用參數可以避免某些依賴關係的產生。在編碼實踐中我們可以通過觀察長參數的方法,如果這個方法經常變動那你就要考慮重構這個方法了。基本原則:事不過三,過三重構。

壞味道:Shotgun Surgery(散彈式修改)

為什麼散彈式修改是一種代碼壞味道?

如果需要修改某個小功能,你需要在眾多不同的類中做修改,首先很難找全,其次很可能會遺漏,這種問題一般稱之為散彈式修改。

  1. public class A { 
  2.     @Value("${db.mysql.url}"
  3.     private String mysqlDbUrl; 
  4.  
  5. public class B { 
  6.     @Value("${db.mysql.url}"
  7.     private String mysqlDbUrl; 

假如有多個類都使用了db.mysql.url這個變量,如果後面要將 mysql 切到 Oracle,那麼可能會涉及到多處修改。如何解決散彈式修改這種代碼壞味道呢?

可以使用 Move Method (搬移函數)和 Move Field (搬移欄位)把所有需要修改的代碼放進同1個類,如果暫時沒有合適的類,就創建一個。

壞味道:Speculative Generality(誇誇其談未來性)

在工作中經常會聽到有開發小夥伴說:昨天加班我將訂單模塊做了修改,未來可以……

聽到這裡你可以會鼓掌:牛叉啊,提前對功能模板預留了擴展性。但是不要急於故障,你看技術總監的臉黑著呢,為什麼呢?這位小夥伴的代碼可能是一種壞味道:誇誇其談未來性。

為什麼誇誇其談未來性是一種代碼壞味道?

網際網路需求迭代更新速度快,」未來可以「意味著當下並不需要,有時候過度的抽象和預留擴展也會讓系統難以理解,並且可能提前背上包袱往前走。

代碼上總是談未來可能性,會讓團隊陷入泥沼。每次有業務變動,開發人員都會考慮各種未來可能性,預留足夠多的擴展接口,這無疑極大增加了代碼複雜度,讓一個可能快速上線的需求變得慢下來。

如何解決誇誇其談未來性這種代碼壞味道呢?

在代碼架構設計中有一個原則叫:Simple Design (簡單設計原則)。

當實現當下業務代碼時需要考慮四個原則:通過測試、揭示意圖、消除重複、最少元素。

當需要為未來而寫的代碼時,可以幹這些:

(1)刪除那些覺的未來有用的參數、代碼、方法調用。

(2)修正方法名,使方法名揭示當下業務場景的意圖,避免抽象的技術描述詞。

如果代碼的改動確實是未來必然會發現的,那麼還是建議保留。誇誇其談未來性更多是指開發人員無依據臆測未來,導致代碼模塊被過度設計。

壞味道:Comments(過多的注釋)

在 《Clean Code》 中列舉了一些常見注釋壞味道:

  • 喃喃自語
  • 多餘的注釋
  • 誤導性注釋
  • 循規方注釋
  • 日誌式注釋
  • 廢話注釋
  • 用注釋來解釋變量意思
  • 用來標記位置的注釋
  • 類的歸屬的注釋
  • 注釋掉的代碼

為什麼過多的注釋是一種代碼壞味道呢?

好的注釋可以輔助開發人員快速閱讀理解代碼,過多的注釋或壞注釋可能會降低代碼的可讀性。

在開發實踐中經常有同學修改了代碼但是注釋沒有同步修改,代碼的實現已經與注釋內容不一致,容易產生誤導。

如何解決過多的注釋這種壞味道呢?

(1)如果代碼塊不再使用請直接刪除不要使用注釋。

(2)方法、變量的命名儘量見名知意,避免用注釋再解釋一遍。

(3)如果較短的注釋不能覆蓋方法的含義,可能是這個方法職責不單一,可以考慮重構這個方法。

總結:

文章列舉了幾種比較常見的代碼壞味道,希望大家在工作編碼中多多練習,爭取人人都能寫出好代碼,讓天下沒有難讀的代碼。

【編輯推薦】

【責任編輯:

武曉燕

TEL:(010)68476606】

點讚 0

相關焦點

  • 是什麼成就了一名「高級」程式設計師?
    下面列舉的事情是大多數高級程式設計師都會做的。 1.至少掌握一門程式語言 我相信有些優秀的程式設計師只懂(並精通)一門程式語言,但在某種程度上而言,這其實會限制一個人的思維。就像當你手拿一把錘子時,任何東西看起來都像釘子。我認為,知道並成功使用至少一門程式語言,這是程式設計師從新手走向老手的重要一步。
  • 用實例告訴你如何重構帶有壞味道的代碼
    在這篇文章中,我將通過 GitHub 上的真實項目來解釋代碼壞味道,並向你展示如何重構這些帶有壞味道的代碼。重複代碼和重複邏輯開發人員通常很懶惰,在某種程度上,這不算一件壞事。然而,因為懶惰而走上了複製黏貼代碼的不歸路那就不對了。這樣可能會導致最常見的代碼壞味道,即邏輯重複,如下所示。
  • 阿里傳奇程式設計師,只會寫代碼,卻從一名員工做到了合伙人
    而在阿里巴巴,也有一位和多隆一樣憨厚忠實的程式設計師工程師,他就是蔡景現,現在是阿里巴巴的合伙人之一,今天就來說說他的故事。阿里傳奇程式設計師,只會寫代碼,卻從一名員工做到了合伙人。1967年,蔡景現出生於浙江省蒼南縣一個貧困的農村家庭。小時候他學習一般,性格也比較內向,但在蔡景現平凡普通的校園成長經歷中,一本basic的程式語言書改變了他的生命歷程。
  • 轉行做程式設計師需要具備哪些條件
    很多轉行的人都會考慮這個問題,下面小編為大家介紹轉行做程式設計師需要具備的條件。一、注重細節如果做事情不注重細節,那麼最好不要做程式設計師。細節在工作中是非常重要的,無論從邏輯方面還是編寫代碼方面,都需要注重細節,在IT方面是不存在差不多的。如果在代碼方面出現細節的錯誤,那將嚴重影響全局。
  • 程式設計師如何寫出高質量的代碼程序
    編碼是程式設計師最重要的工作,每個程式設計師都希望自己可以寫出優雅,高性能,高質量的代碼,對於大師級別的程式設計師,他們的寫的代碼就和藝術品一樣,你會忍不住發出驚嘆,他們怎麼可以創造出如此驚豔的作品出來。下面筆者就以自己的淺薄學識和一些經驗來總結下優秀的程序應該具有的特點。
  • 程式設計師是做什麼的?未來計算機變得智能,就不需要程式設計師了嗎?
    甚至不少程式設計師對他們是做什麼的都沒有清晰的概念。 (不過,我有點關心如果這成為現實後,計算機還需要人類程式設計師嗎?) 現代計算機具有完全邏輯性、直接性和順從性。如果你知道它應該做什麼並且知道如何命令它,那麼和計算機一起工作是快樂的。唯一的問題是,計算機只會做你告訴它去做的。因此,你應該有清晰的思路並且在見你的上司或客戶時保證計算機不出岔子。
  • 高級程式設計師是如何從初級程式設計師演變的?工作經驗不再是唯一途徑!
    區分高級和初級程式設計師的標準是工作年限嗎?程式設計師最重要的工作就是寫代碼嗎? 高級程式設計師是一名犯過其領域內所有可能犯到的錯誤的專家。當我還是一名年輕的初級程式設計師時,我以為自己什麼都懂。我傲慢自大,獨來獨往。我認為自己是「代碼之神」。我不喜歡與他人合作,我認為編寫優秀的代碼是程式設計師最最重要的大事。 我錯了嗎?倒也沒錯。編碼的確很重要——程式設計師必須得去編寫可工作的代碼。但是編寫代碼並不是他唯一重要的事情。 我是在為第一個客戶工作的時候艱難地認識到這一點的。
  • 高效程式設計師的特徵:聰明,懶惰
    我說的這幾個詞的意思是:正確的軟體開發應該是懶惰式開發,也被稱作忍耐式開發;這種開發方式的表現是,在真正動手寫代碼前,程式設計師要花大量的時間通盤考慮所有可能的解決方案和途徑。這可以看作是延緩寫代碼,在沒有完全理解問題前絕不動手寫代碼。先把問題理解清楚,確保將要寫的代碼能真正的解決問題,這將會避免之後寫出大量無用的代碼。
  • 暴漲的比特幣、特斯拉與被程式設計師統治的世界
    但它能反應出一個長期觀察網際網路,並置身其中的人,是如何藉助各種概念和技術的吉光片羽,試圖對一個駁雜的命題做出自己的解釋的努力。它最大限度的保留了一種具有浮世繪意義的囂張和虛妄。這種努力,我上次見,還是在看福山的著作時,又或者是《西部世界》。 01 最優秀的人去做什麼了?
  • 一名普通程式設計師該如何轉人工智慧方向?
    但是,人工智慧並不等同於機器學習,這點在進入這個領域時一定要認識清楚。關於AI領域的發展歷史介紹推薦看周老師寫的《機器學習簡介》。下面一個問題是:AI的門好跨麼?其實很不好跨。我們以機器學習為例。  在學習過程中,你會面對大量複雜的公式,在實際項目中會面對數據的缺乏,以及艱辛的調參等。如果僅僅是因為覺得這個方向未來會「火」的話,那麼這些困難會容易讓人放棄。
  • 國外程式設計師推薦:每個程式設計師都應讀的書
    無論你是初學者,是有經驗的程式設計師,還是軟體項目經理,《程式設計師修煉之道:從小工到專家》都適合你閱讀。推薦數:916就個人而言,這本書目前為止對我影響醉倒的一本編程書。《代碼大全》、《重構》和《設計模式》這些經典書會教給你高效的工作習慣和交易細節。 其他像《人件集》、《計算機編程心理學》和《人月神話》這些書會深入軟體開發的心理層面。
  • 一名40 歲「老」程式設計師的反思 - OSCHINA - 中文開源技術交流社區
    >我是一名程式設計師,幾個月前剛過完四十歲生日。某個星期六的早晨,我參加了一個 React Native 技術交流會,演講者正在竭力說服我們為什麼它會成為移動開發領域真正的下一個大事件。但對我來說,它有點像十五年前的 jsp ,將所有的邏輯都放在演示代碼裡面。但我「老」了,我認為這是因為自己還沒有完全理解它。
  • 普通程式設計師與高級程式設計師有什麼差別?你知道嗎?
    雖然一個程式設計師寫的最多的是代碼,並且幾乎很少有程式設計師是心甘情願的寫文檔,但是一個優秀的程式設計師必須具備一定的寫文檔能力,當然,這個文檔不是說就是Word。比如:Markdown。如果你做的工作現在需要分派給其他人,你有了新的安排,你Handover你的工作的時候,如果你有良好的文檔習慣,那接手工作的人能夠很容易的上手,不然,他看不懂文檔,會來反覆的打擾你。
  • 程式設計師:停電去網吧寫代碼,被當成黑客報警了,網友:想偷我戒指
    甚至在很多人眼裡,這就是年薪百萬,一夜成名的職業了,也讓程式設計師這個行業增添了幾分神秘感,這也讓很多人好奇,究竟是做什麼能夠讓自己收入這麼多呢,他們為何有如此強大的吸金能力呢?有位網友就在論壇上發表自己作為一位程式設計師曾遇到的尷尬事情。
  • 程式設計師的這108個笑話 你都看得懂嗎?-程式設計師,笑話,編程, ——快...
    正好他們摸到工資的時候,一個老程式設計師忽然興奮的大叫:別蠢了,要漲的工資還好好的掛在天上呢!6、諸葛亮是一個優秀的程序猿,每一個錦囊都是應對不同的case而編寫的!但是優秀的程序猿也敵不過更優秀的bug!六出祈山,七進中原,鞠躬盡瘁,死而後已的諸葛亮只因為有一個錯誤的case-馬謖,整個結構就被break了!7、生活中程序猿的真實寫照、一款遊戲一包煙,一臺電腦一下午。
  • 資深程式設計師認為的成功十大黃金定律
    人才對成功來說至關重要,而且人才和天才還是不一樣的,天才大部分是要有天賦的,而人才即使你沒有天賦,遵照規律發展,通過後天努力也可以達到。  如何才能成為一個頂尖的程式設計師呢?Java頂尖程式設計師Lukas Eder和我們分享了偉大程式設計師應該具備的十大特質,他表示這十大特質並不是偉大程式設計師所特有的,任何領域的領導者可以說都有這些特質。
  • 不發項目獎金,程式設計師怒刪代碼,被判 5 個月!
    整理 | 王曉曼責編 | 伍杏玲出品 | 程序人生 (ID:coder _life)之前CSDN 報導過,一外國公司因縮減專利獎金,員工拒絕提交代碼的故事:程式設計師怒了!你敢削減專利獎金,我敢拒絕提交代碼!
  • 程式設計師掉入傳銷組織用「代碼」求救,同事秒懂
    南京一家大型軟體企業的年輕程式設計師小張(化名)萬萬沒有想到,當他被傳銷組織控制、幾近絕望之時,自己嘗試利用計算機「代碼」向同事傳遞求救信息,他的程式設計師同事們竟然「秒懂」,並赴天津協助警方,將他成功解救出來。
  • 程式設計師喜歡在晚上敲代碼是有科學原因的
    網上流傳著一個段子:「程式設計師就是把咖啡因變為編碼的機器。」如果你問大多數開發人員更喜歡在什麼時候工作,大部人會說他們最高效的時間在晚上,對於那些在工作之餘為開源項目做貢獻的人來說更是如此。文中解釋了為什麼大部分工程師,包括他本人,總是遵循著「吸血鬼的生活作息」來寫代碼。他作為一名自由職業者兼連續創業家,有超過17 年以上的軟體開發經驗,自稱 A Geek with a Hat。這篇文章最後還寫成了一本書,可以在Swizec Teller的網站上購買電子書或實體書籍。
  • 中國程式設計師 VS 美國程式設計師,差距就在這五點
    什麼技術火就學什麼,然後進入一個大公司,做幾個成功的項目,積累工作經驗,幾年以後自己當個小管理,拿著十幾萬,或幾十萬的工資,再無追求的過著小日子就好。甚至到了30歲,如果沒能進入管理層,大多數人都會考慮轉行,或僅靠自身的知識累積開始創業。美國程式設計師大多數都是因為熱愛編程行業而從事這份工作的,他們會專注某一個領域幾十年。