用實例告訴你如何重構帶有壞味道的代碼

2020-12-08 51CTO

如果出現了代碼壞味道,說明你的代碼寫得不夠好,需要重構才能讓它們變成乾淨的代碼。在這篇文章中,我將通過 GitHub 上的真實項目來解釋代碼壞味道,並向你展示如何重構這些帶有壞味道的代碼。

重複代碼和重複邏輯

開發人員通常很懶惰,在某種程度上,這不算一件壞事。然而,因為懶惰而走上了複製黏貼代碼的不歸路那就不對了。這樣可能會導致最常見的代碼壞味道,即邏輯重複,如下所示。

為了擺脫這種代碼壞味道,我們需要將紅色部分提取到一個單獨的方法中,這樣就可以在其他地方重用它們。

長方法和臃腫的類

我們都會犯這樣的一個錯誤:在現有方法中添加 if() 或 for() 語句來驗證用戶輸入或檢查用戶是否已登錄。我們其實不應該這樣做。如果一定要做這些驗證,應該創建自己的方法。方法長度應該在 4 到 20 行之間,如果超過 20 行,可以將其中的幾行提取到另一個方法中。同樣的規則也適用於類,根據單一責任原則,方法或類越小越好。

相同或不同類中的重複方法

另一個代碼壞味道是多個方法提供了相同的功能,如下圖所示。

分散式變更(Divergent Change)

如果你了解 SOLID 原則,特別是單一職責原則,那麼你就應該知道,修改一個類的理由應該是單一的。也就是說,User 類不應具有與產品或文件轉換相關的功能。你可以通過將不相關的方法提取到 Product 類或 FileSystem 類來清除這個代碼壞味道。

散彈式變更(Shotgun Surgery)

這與發散變更完全相反。這種代碼壞味道會讓你因為一個需求而去修改多個類。例如,你想要創建一個新的用戶規則(如「Supper-Admin」),然後你發現,為了增加這個規則還需要修改 Profile、Products 和 Employees 類中的某些方法。在這種情況下,可以考慮將這些方法放在一個單獨的類中。

依戀情結(Feature Envy)

有時候,你會在類中找到一個大量使用另一個類的方法。在這種情況下,你可以考慮將這個方法移動到它使用的那個類中。如下圖所示。將 getFullAddress() 從 User 類移動到 ContactInfo 類中豈不是更好?因為它調用了 ContactInfo 類的很多方法。

數據泥團(Data Clumps)

有時候,你會發現很多函數具有相同的參數列表,這樣會導致數泥團代碼壞味道。看看下面的例子,你會發現,幾乎所有類型的預訂都需要護照信息。

在這種情況下,將護照信息移到 PassportInfo 類中,然後將 PassportInfo 對象傳給預訂方法,這樣會更好。這是一個很好的重用代碼的例子。請記住,參數列表太長可能更容易導致 bug 和代碼衝突,而且難以進行單元測試。

痴迷基本類型(Primitive Obsession)

當你在應用程式的所有地方都使用基本數據類型時,就會出現這種代碼壞味道。例如,使用整數表示電話號碼,使用字符串表示貨幣符號。如果你是這麼做的,那麼請先看一下下面這個類。

代碼中的地址被定義為數組,這樣會導致兩個問題,例如,每次我們需要使用地址時都要對其進行硬編碼。那麼,為什麼不創建一個 Address 類呢?

現在,每次我們需要添加或編輯地址時,只需要修改 Address 類。此外,如果我們需要添加一個新的「聯繫我們」方法,就增加一個新的 ContactUs 類。這樣,每個類都有自己的單一職責。

switch 語句

或許你很想知道為什麼使用 switch 語句其實是件很糟糕的事情。雖說使用 switch 語句並不一定總是不好的,但在下面的示例中,你可以看到,switch 語句的代碼塊不僅很大而且是不可提取的。當代碼塊變得越來越大時,你將無法將其拆分成更小的方法。

如果你的 switch 語句代碼塊不是很大,那麼你可以繼續使用它們。例如,工廠模式就使用了 switch 語句。

並行繼承

有時候我會想,並行繼承是不是一種不好的做法。先讓我們來解釋一下並行繼承的概念,然後再討論它是不是代碼壞味道。

從上圖中可以看出,每當我們創建一個新的部門類時,我們還需要創建一個權限類,這將導致之前提到的散彈式變更代碼壞味道。

懶惰類

懶惰類是指只做很少事情的類。還記得這些下面這些代碼嗎?

我們將地址移到一個單獨的類中,但我們沒有對熱線也這麼做,因為它可能只有 3 行代碼。所以,一旦你發現了這些懶惰類,應該將它們移除。

臨時欄位

當一個類實例的某些變量只是偶爾用到,就出出現臨時欄位代碼壞味道。請看下面的例子,你會注意到 $name 和 $contactDetails 只在 notify() 方法中用到。

那麼為什麼不將它們作為方法參數進行傳遞呢。

消息鏈

當一個類使用了另一個類,而那個類又使用了另外一個類,並以此類推,那麼就會出現消息鏈代碼壞味道。在下圖中,你可以看到 Employee->EmployeeConfig->Config。

你可以通過縮短鏈(變成 Employee->Config)讓代碼變得更整潔。

不恰當的親密關係

有時候,一個類的某個方法需要過多地了解另一個類的內部狀態或數據。正如你在下圖中所看到的,notify() 方法位於 User Class 中,但它卻使用了 UserContactDetails 類的很多內部方法。

在這種情況下,***可以將這些邏輯從 User 類移動到 UserContactDetails 類中,並新增 getWelcomeMessage($userName) 方法。

中間人

有時候,你會發現一個類的很多方法什麼事都不做,只是將調用委託給另一個類。在這種情況下,這個類被認為是中間人,大多數時候可以避免使用它。

注意:在 Facade 設計模式中,中間人在某些情況下可能會有所幫助。

接口不同但目的相同的類

通常,因為團隊之間缺乏溝通,創建了兩個不同的類,但它們的作用卻是一樣的,這意味著出現了重複代碼。

不完整的庫

第三方庫並不總能為你提供應用程式中所需的所有功能。在下面的示例中,處理文檔的庫可以通過 ID 獲取一個文檔或一次獲取所有的文檔。

如果你需要獲取特定用戶的所有文檔該怎麼辦?在這種情況下,你需要擴展 Document 類的功能,而不直接修改原始類。這個時候可以使用裝飾模式,如下圖所示。

現在,你可以使用 DocumentsDecorator 類而不是 Documents 類。

注釋

你可能會感到驚訝,但錯誤地使用注釋也是一種代碼壞味道。下面是我的一些建議:

  • 刪除不必要的注釋。
  • 如果代碼很容易理解,請不要添加額外的注釋。
  • 不要留下被注釋的舊代碼。
  • 刪除用於調試的注釋,如 var_dump、echo 等。

誇誇其談未來性(Speculative Generality)

這個代碼壞味道與過早進行優化有關,很多開發人員都沒有注意到這一點。在規劃期間需要考慮的一些注意事項:

  • 不要過度計劃你的代碼。
  • 不要試圖涵蓋只有 1%可能性會在未來發生的情況。
  • 為了讓算法更簡單,可以犧牲一些速度,特別是當你不需要應用程式立即給出結果的時候。
  • 當應用程式速度很慢,即使只有 100 個用戶,也需要進行優化。

英文原文:https://codeburst.io/write-clean-code-and-get-rid-of-code-smells-aea271f30318

【責任編輯:龐桂玉 TEL:(010)68476606】

點讚 0

相關焦點

  • 避免這些代碼壞味道,努力做一名優秀的程式設計師
    重構的手段很簡單:Extract Method,積極抽取函數或方法,隱藏細節保持職責單一。壞味道:Large Class(過大的類)過大的類也常常被成為上帝類(God Class),上帝類一般是指維護了太多功能(違反單一職責原則),連上帝也看不懂你的代碼。
  • 9條消除if……else的錦囊妙計,助你寫出更優雅的代碼
    作者:蘇三說技術 連結:https://juejin.cn/post/6913877637802754061來源:掘金前言最近在做代碼重構,發現了很多代碼的爛味道。其他的不多說,今天主要說說那些又臭又長的if...else要如何重構。
  • 和大家分享JS編程知識之JS內置對象實例詳解
    2)、使用函數來定義對象,然後創建新的對象實例。為了更好的來說明怎樣使用函數來自定義對象,下面用一個實例來說明一下,示例代碼如下:2、獲得當前的日期3、常用的方法:getFullYear():獲取年份getTime():獲取毫秒setFullYear():設置具體的日期getDay():獲取星期下面用一個示例來說明Data對象的常用方法如何使用,示例代碼如下:
  • 一行代碼安裝,TPU也能運行PyTorch,修改少量代碼即可快速移植
    現在福利來了,一個叫做Pytorch Lightning的項目,可以讓你幾乎修改代碼的情況下用上TPU。Pytorch Lightning已經上傳到PyPI,因此只需一行代碼就能安裝這個軟體。幾乎無需修改代碼首先讓我們來看一個MNIST圖像分類網絡的搭建,PyTorch的原始代碼和修改後的PyTorch Lightning代碼幾乎無異。我們只需將nn.Module替換為pl.LightningModule即可。作者表示,相比切換框架,用這種方法重構原來的代碼只需數小時的時間。
  • 如何評價咖啡的味道
    當你看到咖啡時,你會想到「苦」這是一種啟迪,當你品嘗咖啡,品嘗「酸」,這就是認知,當你了解了咖啡的味道,知道中間有「甜」,這就是真正評價一杯咖啡的開始。我們的味覺系統不僅僅依賴張嘴環遊世界,事實上他是3D包圍的,如果你閉著呼吸喝一杯咖啡,在正常呼吸狀態下喝一杯咖啡,味道就不一樣了。
  • 【心理健康】七張圖告訴你,如何與壞情緒相處
    你肯定經常聽到這句話,「與你的壞情緒做朋友」。但是當你身處憤怒、恐懼、悲傷、焦慮……的漩渦,你真的知道應該怎麼做嗎?今天,七張圖告訴你,如何用靜觀自我關懷的方式,與你的困難情緒真正做朋友。「求求你不要找我!」「我不在家!」……或者乾脆把燈關了,假裝沒聽到。這些都是對抗的狀態——我不想讓你進來,或者我連想都不想見你。如何才能做到真的歡迎這些你不喜歡的客人呢?你可以按以下階段練習。
  • 三菱電梯故障代碼以及故障解決方法((實例分析))
    三菱電梯故障代碼大全主板故障代碼:E0 無故障E1 欠速故障E2 超速故障E3 反轉故障E4AST故障(失速)GS釋放時指示三菱電梯故障實例解決方法故障1:最少每3個月1次,抱閘及其觸點應按規範調整,並經常檢查,本例故障是由於油汙的阻力使線圈產生的磁力不足以平衡抱閘強簧力,引起抱閘觸點瞬間接合,向主板傳遞出錯誤信息,電梯急停。
  • 6個步驟,告訴你如何用樹莓派和機器學習DIY一個車牌識別器!(附詳細...
    我們可以用,這是那個模型:https://github.com/ThorPham/License-plate-detection作者用VOTT注釋了收集的車架(當然還有車牌)。最終創建了一個由534張圖像組成的小型數據集,並帶有標記為車牌的邊框。
  • 代碼冗餘,六大Android懶人庫幫你解決
    接下來,我們就把這些庫應用到代碼中,看看它們是如何幫助我們維護代碼乾淨簡潔的。  1.Butter Knife  每訪問一次視圖我們都需要獲取一次該視圖的對象實例,我們可以通過使用rootView.findViewById()方法將返回的對象轉換為正確的視圖類型。
  • 《代碼整潔之道》精讀與演繹】之四 優秀代碼的格式準則
    三、優秀代碼的書寫格式準則  1 像報紙一樣一目了然  想想那些閱讀量巨大的報紙文章。你從上到下閱讀。在頂部,你希望有個頭條,告訴你故事主題,好讓你決定是否要讀下去。第一段是整個故事的大綱,給出粗線條概述,但隱藏了故事細節。
  • 窮人是有味道的,貧窮到底是什麼味道?豆瓣9.3《寄生蟲》告訴你
    這天,基宇的朋友敏赫告訴他自己要做交換生,讓基宇替他去樸社長(李善均)家當家教老師,可以賺到很高的薪水,基宇很開心的就答應了。就這樣,基宇來到了樸社長家中,見到了他的太太(趙汝貞),基宇的學習基礎很紮實,教導多惠綽綽有餘,女主人對他挺滿意,基宇順利的獲得了家教工作。
  • LabVIEW編程實例:如何求解自然常數e
    實例說明自然常數e,是數學中最重要的常數之一,是一個無限不循環小數,也是自然對數函數的底數,其值約為2.71828。它的一個經典的數學定義公式是:使用計算機計算e的值時,可以使用下面的公式近似計算:那麼在LabVIEW中如何編程實現求解這個公式即e的值呢?編程思路從上面的近似公式可以看出,e的值與n的階乘有關,可將上式分解為兩個步驟:求解n的階乘:n!=1×2×3×......
  • 酸甜苦辣……各種食物的味道你知道如何用英語表達嗎?
    但是酸甜苦辣鹹鮮香,各種菜各種味道怎麼用英語來表達,孩子還不知道吧?咱們一起來學學~ 「味道」我們用taste來表示:
  • 分子重構技術_4. 串珠模型重構
    儘管串珠建模非常有用,但務必牢記兩點。首先,即使有高質量的數據,也很容易獲得不良的重構,並且在使用它們之前必須仔細評估重構結果。其次,SAXS在假設檢驗方面比在生成串珠模型上更為準確。換句話說,SAXS擅長指出不是什麼,但不擅長說明是什麼。
  • LabVIEW編程實例:如何求解1000以內的所有素數
    實例說明先看一下什麼是素數:素數也叫質數(primer number),是指一個大於1的自然數,除了1和它自身外,不通整除其它自然數的數,符合這種規律的數就叫素數。素數有無窮多個,那麼在LabVIEW中如何編程實現求解1000以內的所有素數呢?
  • 耳機壞了哪裡修 編輯告訴你應該怎麼辦
    對於一個音樂發燒友來說,耳機可能是他最珍貴的東西,比一切的其他數碼設備都要重要,但相對於一個不怎麼聆聽音樂的人來說,或許他可能根本就用不到這個東西。不過對於一般的年輕人們來說,對於音樂都是擁有一定需求的,耳機還是不能夠沒有。    即使是一個音樂愛好者而非發燒友來說,也會去購買一款能夠滿足自己聆聽需求的耳機,而不是使用隨著手機或者MP3附贈的原配耳機,他們一定會注意保養自己的耳機。
  • 危險品運輸容器的代碼和標記,這裡告訴你
    每一個危險品運輸容器的代碼和標記都是不同的,從事危險品運輸這一行業,不會標記沒關係,但是見到容器上的代碼和標記,我們需要知道它所代表的含義。
  • 程式設計師如何寫出高質量的代碼程序
    當你學會從配置文件中讀取配置,修改配置的時候,你的程序才是優秀的。一定要有測試代碼一個高質量的程序一定會有測試代碼,記住無論程序功能多麼簡單,我們都要寫測試代碼。為什麼TDD會流行,因為很多人懶得寫代碼,而TDD就是強迫你寫測試代碼,因為這樣可以讓代碼更加健壯,同時,其它人修改代碼也可以不會造成更重大影響。
  • 微軟、優步,老工程師告訴你哪些數據結構和算法最重要
    一位在 Uber 等科技公司工作過的開發者分享了他的一手經驗,告訴你實際工作中會用到哪些數據結構和算法。日常工作中,你經常使用算法和數據結構嗎?曾就職於 Uber 等科技公司的工程師 Gergely Orosz 提出了這樣一個問題。此外,他也注意到,越來越多的人覺得算法是無用的,並認為它們只是科技公司提出的一種強制性措施罷了。
  • 如何在Python中編寫簡單代碼,並且速度超越Spark?
    如果你想在Python中編寫簡單代碼,並且用比Spark更快的速度運行,同時無需重新編碼、無需開發者解決部署、擴展和監控問題,可能嗎?你可能會「說我是一個夢想家」。我是一個夢想家,但不是唯一的一個!本篇文章將證明如今可以使用Nuclio和RAPIDSlimg令以上設想成為現實,它們是由NVIDIA孵化的免費開源數據科學加速平臺。