天天看點

從哪些次元評判代碼品質的好壞?如何具備寫出高品質代碼的能力?

  在我的工作經曆中,每當同僚評論起項目代碼品質的時候,聽到的最多的評語就是:“代碼寫得很爛”或者“代碼寫得很好”。用“好”,“爛”這樣的字眼來描述,非常地籠統。當我具體問到底如何爛、如何好的時候,盡管大部分同僚都能簡單地羅列上幾個點,但往往都不夠全面、非常零碎,也切不中要害。

  當然,也有一些工程師對如何評價代碼品質有所認識,比如,好代碼是易擴充、易讀、簡單、易維護的等等,但他們對于這些評價的了解往往隻停留在表面概念上,對于諸多更深入的問題,比如,“怎麼才算可讀性好?什麼樣的代碼才算易擴充、易維護?可讀、可擴充與可維護之間有什麼關系?可維護中‘維護’兩字該如何了解?”等等,并沒有太清晰的認識。

  對于程式員來說,辨識代碼寫得“好”還是“爛”,是一個非常重要的能力。這也是我們寫出好代碼的前提。畢竟,如果我們連什麼是好代碼、什麼是爛代碼,都分辨不清,又談何寫出好代碼呢?

  是以,今天我們就聊一聊關于代碼品質評判的相關問題,希望你在看完本文章的内容之後,對代碼品質的評判有個更加清晰、更加透徹的認識和了解。

如何評價代碼品質的高低

?> 實際上,咱們平時嘴中常說的“好”和“爛”,是對代碼品質的一種描述。“好”籠統地表示代碼品質高,“爛”籠統地表示代碼品質低。對于代碼品質的描述,除了“好”“爛”這樣比較簡單粗暴的描述方式之外,我們也經常會聽到很多其他的描述方式。這些描述方法語義更豐富、更專業、更細化。我搜集整理了一下,羅列在了下面。這些幾乎涵蓋我們所能聽到的描述代碼品質的所有常用詞彙,你可以看一看。

靈活性(flexibility)、可擴充性(extensibility)、可維護性(maintainability)、可讀性(readability)、
可了解性(understandability)、易修改性(changeability)、
可複用(reusability)、可測試性(testability)、子產品化(modularity)、高内聚低耦合(
high cohesion loose coupling)、高效(high effciency)、高性能(highperformance)
、安全性(security)、相容性(compatibility)、易用性(usability)、整潔(clean)、
清晰(clarity)、簡單(simple)、直接(straightforward)、少即是多(less code is 
more)、文檔詳盡(well-documented)、分層清晰(well-layered)、正确性(correctness
、bug free)、健壯性(robustness)、魯棒性(robustness)、可用性(reliability)、
可伸縮性(scalability)、穩定性(stability)、優雅(elegant)、好(good)、壞(bad)

......      

  看到如此多的描述詞,你可能要問了,我們到底該用哪些詞來描述一段代碼的品質呢?實際上,我們很難通過其中的某個或者某幾個詞彙來全面地評價代碼品質。因為這些詞彙都是從不同次元來說的。這就好比,對于一個人的評價,我們需要綜合各個方面來給出,比如性格、相貌、能力、财富等等。代碼品質高低也是一個綜合各種因素得到的結論。我們并不能通過單一的次元去評價一段代碼寫的好壞。比如,即使一段代碼的可擴充性很好,但可讀性很差,那我們也不能說這段代碼品質高。

  除此之外,不同的評價次元也并不是完全獨立的,有些是具有包含關系、重疊關系或者可以互相影響的。比如,代碼的可讀性好、可擴充性好,就意味着代碼的可維護性好。而且,各種評價次元也不是非黑即白的。比如,我們不能簡單地将代碼分為可讀與不可讀。如果用數字來量化代碼的可讀性的話,它應該是一個連續的區間值,而非 0、1 這樣的離散值。

  不過,我們真的可以客觀地量化一段代碼品質的高低嗎?答案是否定的。對一段代碼的品質評價,常常有很強的主觀性。比如,怎麼樣的代碼才算可讀性好,每個人的評判标準都不大一樣。這就好比我們去評價一本小說寫得是否精彩,本身就是一個很難量化的、非常主觀的事情。

  正是因為代碼品質評價的主觀性,使得這種主觀評價的準确度,跟工程師自身經驗有極大的關系。越是有經驗的工程師,給出的評價也就越準确。相反,資曆比較淺的工程師就常常會覺得,沒有一個可執行的客觀的評價标準作為參考,很難準确地判斷一段代碼寫得好與壞。有的時候,自己覺得代碼寫得已經夠好了,但實際上并不是。是以,這也導緻如果沒有人指導的話,自己一個人悶頭寫代碼,即便寫再多的代碼,代碼能力也可能一直沒有太大提高。

最常用的評價标準有哪幾個

  仔細看前面羅列的所有代碼品質評價标準,你會發現,有些詞語過于籠統、抽象,比較偏向對于整體的描述,比如優雅、好、壞、整潔、清晰等;有些過于細節、偏重方法論,比如子產品化、高内聚低耦合、文檔詳盡、分層清晰等;有些可能并不僅僅局限于編碼,跟架構設計等也有關系,比如可伸縮性、可用性、穩定性等。

  為了做到有的放矢、有重點地學習,我挑選了其中幾個最常用的、最重要的評價标準,來詳細講解,其中就包括:​

​可維護性​

​​、​

​可讀性​

​​、​

​可擴充性​

​​、​

​靈活性​

​​、​

​簡潔性(簡單、複雜)​

​​、​

​可複用性​

​​、​

​可測試性​

​。接下來,我們逐一講解一下。

可維護性(maintainability)

?> 我們首先來看,什麼是代碼的“可維護性”?所謂的“維護代碼”到底包含哪些具體工作?

  落實到編碼開發,所謂的“維護”無外乎就是修改 bug、修改老的代碼、添加新的代碼之類的工作。所謂“代碼易維護”就是指,在不破壞原有代碼設計、不引入新的 bug 的情況下,能夠快速地修改或者添加代碼。所謂“代碼不易維護”就是指,修改或者添加代碼需要冒着極大的引入新 bug 的風險,并且需要花費很長的時間才能完成。

  我們知道,對于一個項目來說,維護代碼的時間遠遠大于編寫代碼的時間。工程師大部分的時間可能都是花在修修 bug、改改老的功能邏輯、添加一些新的功能邏輯之類的工作上。是以,代碼的可維護性就顯得格外重要。

  維護、易維護、不易維護這三個概念不難了解。不過,對于實際的軟體開發來說,更重要的是搞清楚,如何來判斷代碼可維護性的好壞。實際上,可維護性也是一個很難量化、偏向對代碼整體的評價标準,它有點類似之前提到的“好”,“壞”“優雅”之類的籠統評價。代碼的可維護性是由很多因素協同作用的結果。代碼的可讀性好、簡潔、可擴充性好,就會使得代碼易維護;相反,就會使得代碼不易維護。更細化地講,如果代碼分層清晰、子產品化好、高内聚低耦合、遵從基于接口而非實作程式設計的設計原則等等,那就可能意味着代碼易維護。除此之外,代碼的易維護性還跟項目代碼量的多少、業務的複雜程度、利用到的技術的複雜程度、文檔是否全面、團隊成員的開發水準等諸多因素有關。

  是以,從正面去分析一個代碼是否易維護稍微有點難度。不過,我們可以從側面上給出一個比較主觀但又比較準确的感受。如果 bug 容易修複,修改、添加功能能夠輕松完成,那我們就可以主觀地認為代碼對我們來說易維護。相反,如果修改一個 bug,修改、添加一個功能,需要花費很長的時間,那我們就可以主觀地認為代碼對我們來說不易維護。你可能會說,這樣的評價方式也太主觀了吧?沒錯,是否易維護本來就是針對維護的人來說的。不同水準的人對于同一份代碼的維護能力并不是相同的。對于同樣一個系統,熟悉它的資深工程師會覺得代碼的可維護性還不錯,而一些新人因為不熟悉代碼,修改 bug、修改添加代碼要花費很長的時間,就有可能會覺得代碼的可維護性不那麼好。這實際上也驗證了我們之前的觀點:代碼品質的評價有很強的主觀性。

可讀性(readability)

?> 軟體設計大師 ​

​Martin Fowler​

​ 曾經說過:“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”翻譯成中文就是:“任何傻瓜都會編寫計算機能了解的代碼。好的程式員能夠編寫人能夠了解的代碼。”Google 内部甚至專門有個認證就叫作 Readability。隻有拿到這個認證的工程師,才有資格在 code review 的時候,準許别人送出代碼。可見代碼的可讀性有多重要,畢竟,代碼被閱讀的次數遠遠超過被編寫和執行的次數。

  我個人認為,代碼的可讀性應該是評價代碼品質最重要的名額之一。我們在編寫代碼的時候,時刻要考慮到代碼是否易讀、易了解。除此之外,代碼的可讀性在非常大程度上會影響代碼的可維護性。畢竟,不管是修改 bug,還是修改添加功能代碼,我們首先要做的事情就是讀懂代碼。代碼讀不大懂,就很有可能因為考慮不周全,而引入新的 bug。

  既然可讀性如此重要,那我們又該如何評價一段代碼的可讀性呢?我們需要看代碼是否符合編碼規範、命名是否達意、注釋是否詳盡、函數是否長短合适、子產品劃分是否清晰、是否符合高内聚低耦合等等。你應該也能感覺到,從正面上,我們很難給出一個覆寫所有評價名額的清單。這也是我們無法量化可讀性的原因。

  實際上,code review 是一個很好的測驗代碼可讀性的手段。如果你的同僚可以輕松地讀懂你寫的代碼,那說明你的代碼可讀性很好;如果同僚在讀你的代碼時,有很多疑問,那就說明你的代碼可讀性有待提高了。

可擴充性(extensibility)

  可擴充性也是一個評價代碼品質非常重要的标準。它表示我們的代碼應對未來需求變化的能力。跟可讀性一樣,代碼是否易擴充也很大程度上決定代碼是否易維護。那到底什麼是代碼的可擴充性呢?

  代碼的可擴充性表示,我們在不修改或少量修改原有代碼的情況下,通過擴充的方式添加新的功能代碼。說直白點就是,代碼預留了一些功能擴充點,你可以把新功能代碼,直接插到擴充點上,而不需要因為要添加一個功能而大動幹戈,改動大量的原始代碼。關于代碼的擴充性,在後面講到“對修改關閉,對擴充開放”這條設計原則的時候,我會來詳細講解,今天我們隻需要知道,代碼的可擴充性是評價代碼品質非常重要的标準就可以了。

靈活性(flexibility)

?> 靈活性也是描述代碼品質的一個常用詞彙。比如我們經常會聽到這樣的描述:“代碼寫得很靈活”。那這裡的“靈活”該如何了解呢?

  盡管有很多人用這個詞彙來描述代碼的品質。但實際上,靈活性是一個挺抽象的評價标準,要給靈活性下個定義也是挺難的。不過,我們可以想一下,什麼情況下我們才會說代碼寫得好靈活呢?我這裡羅列了幾個場景,希望能引發你自己對什麼是靈活性的思考。從剛剛舉的場景來看,如果一段代碼易擴充、易複用或者易用,我們都可以稱這段代碼寫得比較靈活。是以,靈活這個詞的含義非常寬泛,很多場景下都可以使用。

簡潔性(simplicity)

?> 有一條非常著名的設計原則,你一定聽過,那就是 KISS 原則:“Keep It Simple,Stupid”。這個原則說的意思就是,盡量保持代碼簡單。代碼簡單、邏輯清晰,也就意味着易讀、易維護。我們在編寫代碼的時候,往往也會把簡單、清晰放到首位。

  不過,很多程式設計經驗不足的程式員會覺得,簡單的代碼沒有技術含量,喜歡在項目中引入一些複雜的設計模式,覺得這樣才能展現自己的技術水準。實際上,思從深而行從簡,真正的高手能雲淡風輕地用最簡單的方法解決最複雜的問題。這也是一個程式設計老手跟程式設計新手的本質差別之一。

  除此之外,雖然我們都能認識到,代碼要盡量寫得簡潔,符合 KISS 原則,但怎麼樣的代碼才算足夠簡潔?不是每個人都能很準确地判斷出來這一點。是以,在後面的章節中,當我們講到 KISS 原則的時候,我會通過具體的代碼執行個體,詳細給你解釋,“為什麼 KISS 原則看似非常簡單、好了解,但實際上用好并不容易”。今天,我們就暫且不展開詳細講解了。

可複用性(reusability)

  當我們添加一個新的功能代碼的時候,原有的代碼已經預留好了擴充點,我們不需要修改原有的代碼,隻要在擴充點上添加新的代碼即可。這個時候,我們除了可以說代碼易擴充,還可以說代碼寫得好靈活。

  當我們要實作一個功能的時候,發現原有代碼中,已經抽象出了很多底層可以複用的子產品、類等代碼,我們可以拿來直接使用。這個時候,我們除了可以說代碼易複用之外,還可以說代碼寫得好靈活。

  當我們使用某組接口的時候,如果這組接口可以應對各種使用場景,滿足各種不同的需求,我們除了可以說接口易用之外,還可以說這個接口設計得好靈活或者代碼寫得好靈活。

  代碼的可複用性可以簡單地了解為,盡量減少重複代碼的編寫,複用已有的代碼。在後面的很多章節中,我們都會經常提到“可複用性”這一代碼評價标準。

  比如,當講到面向對象特性的時候,我們會講到繼承、多态存在的目的之一,就是為了提高代碼的可複用性;當講到設計原則的時候,我們會講到單一職責原則也跟代碼的可複用性相關;當講到重構技巧的時候,我們會講到解耦、高内聚、子產品化等都能提高代碼的可複用性。可見,可複用性也是一個非常重要的代碼評價标準,是很多設計原則、思想、模式等所要達到的最終效果。

!> 實際上,代碼可複用性跟 DRY(Don’t Repeat Yourself)這條設計原則的關系挺緊密的,是以,在後面的章節中,當講到 DRY 設計原則的時候,我還會講更多代碼複用相關的知識,比如,“有哪些程式設計方法可以提高代碼的複用性”等。

可測試性(testability)

  相對于前面六個評價标準,代碼的可測試性是一個相對較少被提及,但又非常重要的代碼品質評價标準。代碼可測試性的好壞,能從側面上非常準确地反應代碼品質的好壞。代碼的可測試性差,比較難寫單元測試,那基本上就能說明代碼設計得有問題。關于代碼的可測試性,我們在重構那一部分,會花時間來詳細介紹。現在,你暫時隻需要知道,代碼的可測試性非常重要就可以了。

如何才能寫出高品質的代碼

  我相信每個工程師都想寫出高品質的代碼,不想一直寫沒有成長、被人吐槽的爛代碼。那如何才能寫出高品質的代碼呢?針對什麼是高品質的代碼,我剛剛講到了七個最常用、最重要的評價名額。是以,問如何寫出高品質的代碼,也就等同于在問,如何寫出易維護、易讀、易擴充、靈活、簡潔、可複用、可測試的代碼。

  要寫出滿足這些評價标準的高品質代碼,我們需要掌握一些更加細化、更加能落地的程式設計方法論,包括面向對象設計思想、設計原則、設計模式、編碼規範、重構技巧等。而所有這些程式設計方法論的最終目的都是為了編寫出高品質的代碼。

  比如,面向對象中的繼承、多态能讓我們寫出可複用的代碼;編碼規範能讓我們寫出可讀性好的代碼;設計原則中的單一職責、DRY、基于接口而非實作、裡式替換原則等,可以讓我們寫出可複用、靈活、可讀性好、易擴充、易維護的代碼;設計模式可以讓我們寫出易擴充的代碼;持續重構可以時刻保持代碼的可維護性等等。具體這些程式設計方法論是如何提高代碼的可維護性、可讀性、可擴充性等等的呢?在後面的文章中慢慢來學習。

重點回顧

如何評價代碼品質的高低

  • 代碼品質的評價有很強的主觀性,描述代碼品質的詞彙也有很多,比如可讀性、可維護性、
  • 靈活、優雅、簡潔等,這些詞彙是從不同的次元去評價代碼品質的。它們之間有互相作用,
  • 并不是獨立的,比如,代碼的可讀性好、可擴充性好就意味着代碼的可維護性好。代碼品質
  • 高低是一個綜合各種因素得到的結論。我們并不能通過單一的次元去評價一段代碼的好壞。

最常用的評價标準有哪幾個

  • 最常用到幾個評判代碼品質的标準是:可維護性、可讀性、可擴充性、靈活性、簡潔性、可
  • 複用性、可測試性。其中,可維護性、可讀性、可擴充性又是提到最多的、最重要的三個評
  • 價标準。

如何才能寫出高品質的代碼

  • 要寫出高品質代碼,我們就需要掌握一些更加細化、更加能落地的程式設計方法論,這就包含面
  • 向對象設計思想、設計原則、設計模式、編碼規範、重構技巧等等,這也是我後面文章學習
  • 的重點。

繼續閱讀