相信博客園的讀者大多都是千萬“碼農”中的一員,每個人都寫過很多代碼,但並不是每一個人都能寫出高質量的代碼。rome is not built in one day !——完成高質量的代碼也不是一蹴而就的。為了寫出高質量的代碼,我們需要藉助一些手段,“代碼重構”基本上是最常用的手段,甚至是唯一的手段。 ...
相信博客園的讀者大多都是千萬“碼農”中的一員,每個人都寫過很多代碼,但並不是每一個人都能寫出高質量的代碼。
rome is not built in one day !——完成高質量的代碼也不是一蹴而就的。為了寫出高質量的代碼,我們需要藉助一些手段,“代碼重構”基本上是最常用的手段,甚至是唯一的手段。
重構需要你花一些心思去琢磨自己的代碼,這好比自己種的花花草草,看你怎麼對待它們。你不給它們澆水、除蟲、曬太陽,它們可能會長蟲、生病。如果你用心地去對待它們,它們可能會枝繁葉茂,花開茂盛。但是,即使你已經很專心地去打理它們了,它們也不一定是“健康的”,畢竟每一種花草都有自己的習性,代碼也是如此。
為何要重構?
做一件事情之前,我們都應該先反問自己Why。
我也問過自己:為什麼要對代碼進行重構?項目一個接一個地做,我哪有什麼時間去重構代碼,我還要控制團隊的項目成本!
Oh, Shit!為了讓代碼看起來更好一些,我們花了一些時間在代碼重構上,項目的成本又超了!向公司領導彙報時,又要編造合適的理由了!
我的實際理由是:我們不應該僅僅實現可執行的系統功能,更應該提供高質量的代碼,提升系統的設計以讓其適應更多的變化,如果超出的成本是在可以接受的範圍內,它將符合我們“將來的利益”。
持續利益的提升
何謂“將來的利益”?如果代碼的質量較為可靠,則會為以後的維護、升級奠定良好的基礎。倘若已完成的代碼質量不高、不夠整潔,在系統維護、升級時,不說花在代碼修改上的時間,僅閱讀理解代碼就將花去你大量的時間。如果你需要對項目持續地維護、升級,這該是多麼長久的一個痛苦啊!到這裡,你能理解了吧,這些”低質量代碼“產生的將來成本將遠超於你現在重構的成本。正所謂“長痛不如短痛”,要自宮就趕緊的,這樣才能早點練成“葵花寶典”。
2B地說,重構是為了將來不要掉進自己挖的坑裡。
普通地說,重構是為了提高代碼的可讀性和可維護性。
文藝地說,重構是為了讓代碼的身姿妖嬈美如畫。
設計的提升
沒有完美的產品,也沒有完美的設計,設計的最終目的是為用戶創造價值。大多數系統或產品的設計是伴隨著需求持續演變的,設計調整時,重構又是必備的利器。重構的過程,是對設計過程的整理,也是對設計細節的推敲。設計既然是不完美的,那麼在重構的思考過程中我們總是能發現一些設計上的瑕疵。發現了瑕疵,我們就可以通過重構加以改善。小到對欄位、方法的重構,大到對工程結構、系統架構的重構,都是設計的改變。
這個過程可能讓你痛苦,可能讓你快樂。
何時才重構?
假如你是生活在大山裡的樵夫,為了維持每天的生計,你覺得什麼時間上山砍柴最合適?
是早晨、午後、傍晚、還是晚上?我覺得在早晨和午後較為合適,早晨是最佳時機。
為什麼呢?早晨是人精神最飽滿的時候,也是時間最充分的時候,你可以預先做一些準備,比如:花些時間計劃砍柴的工作量、磨好斧子、準備好捆柴的工具。
如果你選擇午後,你將沒有時間磨斧子,會影響砍柴的效率。
如果你選擇傍晚,砍完柴了天都已經黑了,你是準備住山上嗎?夜間山裡面有各種野生動物,你是想心驚膽戰地過一夜嗎?
如果你選擇晚上,能不能正常上山都是個問題,你別迷路在山間,後半夜的時間都花去早出路了。
講這個例子,並不是為了體現做什麼事情都一定要趁早,而是為了說明無論做什麼事情,選擇合適的時機非常重要。當然趁早做一些事情,也能更早發現問題。
重構亦是如此。重構是一把利器,選擇合適的時機重構或許會讓我們的收益超出預期。
下麵幾項是我覺得可能需要重構的時機:
- 代碼中存在著重覆的代碼
- 代碼中存在過大的類或過長的方法
- 代碼中存在強依賴、緊耦合的結構
- 代碼的運算邏輯難以理解
- 代碼不能清晰地描述現實對象的特征、行為以及對象間的關係
當然,每個人對重構、對系統、對業務、對用戶的理解是不同的,所以每個人選擇重構的時機也盡不相同。你大可不必參考這幾項,結合周圍的環境選擇合適自己的(這本身也是一個時機選擇)就好。即使出現這幾種情形,重構也不一定是必須的,這取決於你們的項目經驗、你們心中的準則,也取決於你們看待產品、看待項目的方式。
重構的策略
代碼重構有很多策略,我將基於《31 Days Refactoring》和《Clean Code》這兩本書,以及OOP(面向對象編程)的概念介紹一些常用的重構策略。當然,我不是將書中的內容搬到這裡,我會儘可能地加入自己的理解。
下麵列出了我將要講的重構系列的主題,它們基本都來源於《31 Days Refactoring》。是的,這本書只有50多頁,你懂點英文,這本書你花1個小時或許就看完了。重構的代碼大家都能讀懂,我不想徒有其“表”,我想更深層次地理解其“意”,不僅於自己,也於讀到這一系列文章的人。
方法、欄位重構
- 移動方法
- 提取方法、提取方法對象
- 方法、欄位的提升和降低
- 分解方法
- 為布爾方法命名
- 引入對象參數
類、介面重構
- 使用委派代替繼承
- 提取介面
- 解除依賴
- 分離職責
- 提取基類
- 提取子類
- 合併子類
- 去除上帝類
- 去除中間類
- 使用多態代替條件判斷
設計模式重構
- 策略模式代替Switch
- 引入契約式設計
- 提取工廠類
一般性重構
- 重命名
- 分解複雜判斷
- 用條件判斷代替異常
- 避免雙重否定
- 儘快返回
- 用條件判斷代替異常
- 封裝條件
- 封裝集合
你們也看到了,列舉的這些重構策略都是一些基本技巧,即使學會這些技巧也不能讓你馬上成為“重構”大師,我自身也不是什麼代碼高手。
重構的雙刃劍
事物總是存在兩面性的,重構亦是如此。重構是一把雙刃劍,用得好則能卸磨殺驢,用不好則傷己傷人。
並不是每一次重構,都能然讓你的收益大於支出。重構可能會產生新的bug,加重項目的開發負擔,甚至讓項目版本回滾(這些事情我都已經碰過瓷兒了)。在設計層面,不當地重構可能會破壞原有的設計,甚至破壞系統現有的功能。
正所謂“不破不立”,你不去用劍,不和劍交流,不被劍傷,你永遠無法成為一名好劍客。
後話
最後,我想引用屈原的一句名言:“路漫漫其修遠兮,吾將上下而求索”。
我無法讓你們在看完這一系列文章後,就能“深度”領會重構的“意”了,這仍然需要你自己去琢磨代碼層面的一些事兒,也許是一個方法,也許是項目的架構。
另外,我個人是不推薦“1天學會xxx”、“3天掌握xxx”、”8天弄懂xxx“的。你讀完了文章,不代表你真的理解了。你需要自己去思考,自己去實踐。1天也好,3天也罷,若你肯花時間專心地去領會一件事情,並堅持不懈地為之努力,你遲早是懂的。他人講得天花亂墜,你聽過看過之後,也不過是過眼雲煙。
讀到這一系列文章的讀者,如果有不同的見解,或者察覺文章中的描述有誤,歡迎來討論或指正。通過這一系列文章,我希望讀者會有所收益,也希望自己對重構能有一個更深的理解。如果您覺得文章對您有用,請不要吝嗇您的“推薦”;但如果您覺得它一無是處,請您不要客氣地點擊“反對”,您的反對也是給予我的一種磨礪,它會讓我持續學習和改進。
“多麼痛的領悟~,維護曾是你的全部~~。願你掙脫爛代碼的枷鎖,各種坑的束縛,別再為項目受苦~~~”。結尾獻給各位一首歌,祝大家周末愉快!