設計原則與思想:規(guī)范與重構(gòu)

什么情況下要重構(gòu)?到底重構(gòu)什么?又該如何重構(gòu)?

重構(gòu)的目的:為什么要重構(gòu)(why)?

在保持功能不變的前提下,利用設計思想、原則、模式、編程規(guī)范等理論來優(yōu)化代碼,修改設計上的不足,提高代碼質(zhì)量。

對于項目來言,重構(gòu)可以保持代碼質(zhì)量持續(xù)處于一個可控狀態(tài),不至于腐化到無可救藥的地步。對于個人而言,重構(gòu)非常鍛煉一個人的代碼能力,并且是一件非常有成就感的事情。它是我們學習的經(jīng)典設計思想、原則、模式、編程規(guī)范等理論知識的練兵場。

重構(gòu)的對象:到底重構(gòu)什么(what)?

按照重構(gòu)的規(guī)模,我們可以將重構(gòu)大致分為大規(guī)模高層次的重構(gòu)和小規(guī)模低層次的重構(gòu)。

大規(guī)模高層次重構(gòu)包括對代碼分層、模塊化、解耦、梳理類之間的交互關系、抽象復用組件等等。這部分工作利用的更多的是比較抽象、比較頂層的設計思想、原則、模式。

小規(guī)模低層次的重構(gòu)包括規(guī)范命名、注釋、修正函數(shù)參數(shù)過多、消除超大類、提取重復代碼等等編程細節(jié)問題,主要是針對類、函數(shù)級別的重構(gòu)。小規(guī)模低層次的重構(gòu)更多的是利用編碼規(guī)范這一理論知識。

重構(gòu)的時機:什么時候重構(gòu)(when)?

一定要建立持續(xù)重構(gòu)意識,把重構(gòu)作為開發(fā)必不可少的部分,融入到日常開發(fā)中,而不是等到代碼出現(xiàn)很大問題的時候,再大刀闊斧地重構(gòu)。

重構(gòu)的方法:又該如何重構(gòu)(how)?

大規(guī)模高層次的重構(gòu)難度比較大,需要組織、有計劃地進行,分階段地小步快跑,時刻讓代碼處于一個可運行的狀態(tài)。而小規(guī)模低層次的重構(gòu),因為影響范圍小,改動耗時短,所以,只要你愿意并且有時間,隨時隨地都可以去做。

為了保證重構(gòu)不出錯,有哪些非常能落地的技術手段?

什么是單元測試?

集成測試的測試對象是整個系統(tǒng)或者某個功能模塊,是一種端到端(end to end)的測試。而單元測試的測試對象是類或者函數(shù),用來測試一個類和函數(shù)是否都按照預期的邏輯執(zhí)行。這是代碼層級的測試。

為什么要寫單元測試?

  1. 單元測試能有效地幫你發(fā)現(xiàn)代碼中的 bug
  2. 寫單元測試能幫你發(fā)現(xiàn)代碼設計上的問題
  3. 單元測試是對集成測試的有力補充
  4. 寫單元測試的過程本身就是代碼重構(gòu)的過程
  5. 閱讀單元測試能幫助你快速熟悉代碼
  6. 單元測試是 TDD 可落地執(zhí)行的改進方案

如何編寫單元測試?

  1. 寫單元測試真的是件很耗時的事情嗎?
  2. 對單元測試的代碼質(zhì)量有什么要求嗎?
  3. 單元測試只要覆蓋率高就夠了嗎?
  4. 寫單元測試需要了解代碼的實現(xiàn)邏輯嗎?
  5. 如何選擇單元測試框架?

什么是代碼的可測試性?如何寫出可測試性好的代碼?

什么是代碼的可測試性?

粗略地講,所謂代碼的可測試性,就是針對代碼編寫單元測試的難易程度。對于一段代碼,如果很難為其編寫單元測試,或者單元測試寫起來很費勁,需要依靠單元測試框架中很高級的特性,那往往就意味著代碼設計得不夠合理,代碼的可測試性不好。

編寫可測試性代碼的最有效手段

依賴注入是編寫可測試性代碼的最有效手段。通過依賴注入,我們在編寫單元測試的時候,可以通過 mock 的方法解依賴外部服務,這也是我們在編寫單元測試的過程中最有技術挑戰(zhàn)的地方。

常見的測試不友好的代碼

  • 代碼中包含未決行為邏輯
  • 濫用可變?nèi)肿兞?/li>
  • 濫用靜態(tài)方法
  • 使用復雜的繼承關系
  • 高度耦合的代碼

如何通過封裝、抽象、模塊化、中間層等解耦代碼?

“解耦”為何如此重要?

過于復雜的代碼往往在可讀性、可維護性上都不友好。解耦保證代碼松耦合、高內(nèi)聚,是控制代碼復雜度的有效手段。代碼高內(nèi)聚、松耦合,也就是意味著,代碼結(jié)構(gòu)清晰、分層模塊化合理、依賴關系簡單、模塊或類之間的耦合小,那代碼整體的質(zhì)量就不會差。

代碼是否需要“解耦”?

間接的衡量標準有很多,比如,看修改代碼是否牽一發(fā)而動全身。直接的衡量標準是把模塊與模塊、類與類之間的依賴關系畫出來,根據(jù)依賴關系圖的復雜性來判斷是否需要解耦重構(gòu)。

如何給代碼“解耦”?

  1. 封裝與抽象
  2. 中間層
  3. 模塊化
  4. 其他設計思想和原則
  • 單一職責原則
  • 基于接口而非實現(xiàn)編程
  • 依賴注入
  • 多用組合少用繼承
  • 迪米特法則

讓你最快速地改善代碼質(zhì)量的20條編程規(guī)范

命名與注釋(Naming and Comments)

  1. 關于命名
  • 命名的關鍵是能準確達意。對于不同作用域的命名,我們可以適當?shù)剡x擇不同的長度。作用域小的變量(比如臨時變量),可以適當?shù)剡x擇短一些的命名方式。除此之外,命名中也可以使用一些耳熟能詳?shù)目s寫。

  • 我們可以借助類的信息來簡化屬性、函數(shù)的命名,利用函數(shù)的信息來簡化函數(shù)參數(shù)的命名。

  • 命名要可讀、可搜索。不要使用生僻的、不好讀的英文單詞來命名。除此之外,命名要符合項目的統(tǒng)一規(guī)范,不要用些反直覺的命名。

  • 接口有兩種命名方式:一種是在接口中帶前綴“I”;另一種是在接口的實現(xiàn)類中帶后綴“Impl”。對于抽象類的命名,也有兩種方式,一種是帶上前綴“Abstract”,一種是不帶前綴。這兩種命名方式都可以,關鍵是要在項目中統(tǒng)一。

  1. 關于注釋
  • 注釋的目的就是讓代碼更容易看懂。只要符合這個要求的內(nèi)容,你就可以將它寫到注釋里。總結(jié)一下,注釋的內(nèi)容主要包含這樣三個方面:做什么、為什么、怎么做。對于一些復雜的類和接口,我們可能還需要寫明“如何用”。

  • 注釋本身有一定的維護成本,所以并非越多越好。類和函數(shù)一定要寫注釋,而且要寫得盡可能全面、詳細,而函數(shù)內(nèi)部的注釋要相對少一些,一般都是靠好的命名、提煉函數(shù)、解釋性變量、總結(jié)性注釋來提高代碼可讀性。

代碼風格(Code Style)

  • 函數(shù)的代碼行數(shù)不要超過一屏幕的大小,比如 50 行。類的大小限制比較難確定。

  • 最好不要超過 IDE 顯示的寬度。當然,限制也不能太小,太小會導致很多稍微長點的語句被折成兩行,也會影響到代碼的整潔,不利于閱讀。

  • 對于比較長的函數(shù),為了讓邏輯更加清晰,可以使用空行來分割各個代碼塊。在類內(nèi)部,成員變量與函數(shù)之間、靜態(tài)成員變量與普通成員變量之間、函數(shù)之間,甚至成員變量之間,都可以通過添加空行的方式,讓不同模塊的代碼之間的界限更加明確。

  • 比較推薦使用兩格縮進,這樣可以節(jié)省空間,特別是在代碼嵌套層次比較深的情況下。除此之外,值得強調(diào)的是,不管是用兩格縮進還是四格縮進,一定不要用 tab 鍵縮進。

  • 比較推薦將大括號放到跟上一條語句同一行的風格,這樣可以節(jié)省代碼行數(shù)。但是,將大括號另起一行,也有它的優(yōu)勢,那就是,左右括號可以垂直對齊,哪些代碼屬于哪一個代碼塊,更加一目了然。

  • 在 Google Java 編程規(guī)范中,依賴類按照字母序從小到大排列。類中先寫成員變量后寫函數(shù)。成員變量之間或函數(shù)之間,先寫靜態(tài)成員變量或函數(shù),后寫普通變量或函數(shù),并且按照作用域大小依次排列。

編程技巧(Coding Tips)

  • 將復雜的邏輯提煉拆分成函數(shù)和類。

  • 通過拆分成多個函數(shù)或?qū)?shù)封裝為對象的方式,來處理參數(shù)過多的情況。

  • 函數(shù)中不要使用參數(shù)來做代碼執(zhí)行邏輯的控制。

  • 函數(shù)設計要職責單一。

  • 移除過深的嵌套層次,方法包括:去掉多余的 if 或 else 語句,使用 continue、break、return 關鍵字提前退出嵌套,調(diào)整執(zhí)行順序來減少嵌套,將部分嵌套邏輯抽象成函數(shù)。

  • 用字面常量取代魔法數(shù)。

  • 用解釋性變量來解釋復雜表達式,以此提高代碼可讀性。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容