摘要:讓咱們再回到重構的基本概念,思考咱們須要怎樣的重構輔助服務。
代碼重構是每一位開發者最熟悉不過的字眼,其出現一般伴隨着開發過程。在程序開發、迭代與演進的漫漫長路中,某次不經意的修改就可能破壞程序原有的設計與結構,形成代碼結構的流失,而這種流失是具備累積性的,若未及時發現與重構,程序就會逐漸腐爛甚至變質,造成巨大的歷史債務。其實重構就比如收拾房間,若是咱們每天打掃,那麼天天花3分鐘就能打掃乾淨,可若是一個月不打掃,你想一想須要多久才能打掃完。程序員
既然代碼重構在開發過程當中這麼重要,怎麼能沒有相應的服務來支撐它呢?咱們能不能開發出相應的「掃帚」輔助咱們天天打掃房間?亦或是「掃地機器人」自動的幫咱們打掃一個月未收拾的房間?設計模式
帶着上述疑問,讓咱們再回到重構的基本概念,思考咱們須要怎樣的重構輔助服務。安全
如此書中所說,所謂重構(Refactoring)是這樣一個過程:在不改變代碼外在行爲的前提下,對代碼作出修改,以改進程序的內部結構。這裏的重構有兩層含義,一個名詞含義,一個動詞含義:架構
重構(名詞):對軟件內部結構的一種調整, 目的是在不改變軟件可觀察行爲的前提下,提升其可理解性, 下降其修改爲本。運維
重構(動詞):使用一系列重構手法, 在不改變軟件可觀察行爲的前提下,調整其結構。函數
至此,咱們應該明白,一款好的重構輔助服務應該至少兼具不變與變兩個特徵:不改變軟件可觀測行爲;優化代碼結構,下降修改爲本,提升可理解性。工具
就重構時機問題,業界也有比較激烈的討論,有人認爲重構應該隨時隨地地進行,不該該爲了重構而重構,就好比我在添加新功能時、修補錯誤時、或者複審代碼時均可以進行重構,咱們暫且稱之爲「開發時重構」,也有人認爲「添加新功能」和「重構」是兩頂帽子,在添加新功能時,就不該該修改既有代碼,只管添加新功能,而在重構時,就不能添加功能,只管改進程序結構,一次只作一件事,咱們暫且稱之爲「維護式重構」。我我的認爲這兩種說法並不矛盾,真正好的重構應該是二者的有機結合。優化
如上圖所示,紅色範圍是我認爲比較好的重構實踐。ui
我認爲不管在作新功能開發時仍是在作老版本維護時,都適合作代碼重構,只是適合的重構粒度不一樣而已。對於開發時重構,比較適合作我的級小範圍的微重構,這種重構每每影響範圍小,且較爲簡單,不會對開發增長工做難度,例如「重命名」、「函數提取」等原子重構;對於維護時重構,比較適合作架構級大範圍的複雜重構,這種重構每每是爲了解決項目代碼中遺留的技術債務,且一般和代碼壞味道的消除結合在一塊兒,例如依戀情結(Feature Envy)、數據泥團(Data Clumps)等,而這些壞味道的重構每每由一系列的重構原子操做組合而成。固然,最好將重構工做盡量多地作在開發階段,儘可能減小新增代碼對已有設計與架構的破壞。編碼
看到這裏,咱們是否有些似曾相識,這不就對應了上文中提到的「掃帚」以及「掃地機器人」在實際重構工做中的應用?
使用過現代IDE開發代碼的同窗們應該都知道,以IntelliJ IDEA爲表明的不少IDE多少都自帶一些重構功能,但目前爲止,這些重構都是例如「重命名」、「函數提取」等原子性重構,只對重構過程提供了部分支持,絕大部分的代碼、架構壞味道重構工做仍然得靠手工完成,就比如你須要打掃一個月未打掃的房子,但手中只有一把掃帚同樣。Kent Beck 說過:「手工重構仍然是很耗時的工做。正是這個簡單的事實形成了不少程序願不肯意進行重構,儘管他們知道本身應該重構,但畢竟重構的成本太大了。若是可以把重構變的像調整代碼格式那麼簡單,程序員天然也會樂意像整理代碼格式那樣整理系統的設計。而這樣的整理對代碼的可讀性、可複用性和可理解性,都能帶來深遠的正面影響。」正因如此,一款智能的、能夠幫助開發者發現代碼、架構中的壞味道而且引導開發者完成代碼重構的服務尤其重要。
對於持續交付過程當中的代碼開發與發佈環節,如上圖虛線框所示,幾乎每一步都與代碼質量的看護或代碼重構相關。
我的認爲重構大體能夠分爲4個層級,這些層級之間的關係能夠從上圖看出,並不是是自底向上依次包含的關係,它們之間會有些重疊也有些不一樣。單純從重構自己來說,越上層級的重構操做非肯定性越強,更偏業務性,而越下層級的重構操做肯定性越強,更偏技術性。從智能重構服務的角度來說,越上層越偏檢測能力,越下層越偏純重構能力。下面咱們由下至上依次來看看不一樣層級重構操做的特色與異同。
原子重構能力:上文中也有提到原子重構,原子重構到底有哪些呢?顧名思義,即不可再分的重構操做,例如重命名、移動、刪除等重構操做,這些重構操做是徹底肯定性的,例如我想要抽取一段代碼造成一個新方法,是否可抽,能夠抽成什麼樣都是徹底肯定的。這些重構操做由於其不可再分性位於重構最底層。
編碼規範重構:有些同窗可能會對這個層級的重構比較陌生,其實它就是基於規範和規則的重構,一般包含了咱們所說的微重構,例如對違反了命名風格的標識符進行重命名重構,亦或是對無用代碼進行刪除的安全刪除重構等等,而且這層的重構操做徹底能夠經過調用下層的1個原子重構來實現。正由於這些重構操做基於規則,其重構結果也是相對肯定的。
代碼壞味道重構:代碼壞味道也包括了架構壞味道,例如Feature Envy、God Class等類型,這些層級重構的目的是爲了消除相應的代碼壞味道,因此相對來講更復雜,一次重構的完成每每要調用下層多個原子重構操做,例如對God Class進行重構,須要調用至少一次Extract Class原子重構。同時這層重構的非肯定性也更高,對一種代碼壞味道的重構消除每每能夠經過多種手段。
設計模式壞味道重構:這層重構應該屬於金字塔頂端的重構,由於它涉及範圍太廣,更偏向於對業務的理解與預測,從具象到抽象,顯得有些虛無縹緲。設計模式有7中壞味道和11中原則,目前不管學術界仍是工業界對於這類問題檢測、重構相關的研究都還很少。
結合Devops下的重構服務需求與上圖中的四層重構,咱們基本能夠看出智能重構服務不一樣層級的重構能力主要運用在開發工做流中的哪一個階段。對於原子重構來講,由於其肯定性以及業務無關性,最適合做爲插件集成在IDE上,由開發者作增量開發時調用;對於編碼規範重構來講,適合同原子重構一塊兒集成進IDE插件,在開發過程當中自動且快速的識別到違反特定規則的代碼,並輔助開發者重構;對於代碼壞味道重構來講,適合在持續發佈環節的門禁階段攔截問題並引導開發者回到IDE進行代碼智能重構;對於設計模式壞味道重構來講,由於其涉及業務理解與預測,更適合放進生產運維環節,結合版本演進與迭代歷史,給出架構腐化的預測以及相應重構方案的推薦。
既然上層重構操做均可以轉化爲最底層的原子重構操做,那如何表達這種轉化呢?表格中列舉了幾種常見的重構原子操做,每種重構原子操做均可以用一個函數來表達。例如Move Method,函數名爲Move Method,咱們用三個參數來明確它的行爲:Source(所屬類型)、Target(目標類型)以及(須要移動的方法),有了這些信息就能夠明確一次Move Method原子重構。對於任何一個複雜的重構來講,均可以表示成以下形式的原子重構序列,即一系列原子重構的組合。
就像搭積木同樣,任何上層重構均可以經過搭積木的方式組合底層原子重構來實現。
重構服務的設計亦如其它不少開發服務同樣,最終目標都是提高用戶開發效率與代碼質量,若是兩者皆得不到保證,那這款服務將被永遠丟進垃圾堆裏。從咱們在華爲內部的實踐經驗,總結了如下幾點原則:
充分的用戶交互:經典的重構工做流程爲「小步前進,隨時可用,隨時可停,隨時回退」,小步修改意味着每一步出錯的可能性大大減少,要遵循這個流程,就離不開工具和用戶頻繁的交互,要引導用戶一步步的完成複雜的代碼重構,每一個過程都要作到隨時可用,隨時可停,隨時可退。
友好的重構工做界面:代碼重構有點相似代碼自動修復,但比代碼自動修復涉及範圍更廣,如何讓用戶更好的表達重構意圖、而且一目瞭然的看到重構對原代碼架構的影響很是重要,這一部分有不少能夠創新的地方。
個性化用戶配置:上層級的重構每每能夠有不一樣的執行路徑,根據代碼工程不一樣、業務場景不一樣,同一種壞味道重構的實現方式也可能不一樣,要以用戶爲中心,根據業務不一樣、用戶不一樣給出個性化、定製化的重構解決方案,這一部分恰好是AI擅長的領域。
Devops服務深度集成:任何一款開發服務若是離開Devops服務就會成爲一個孤立的散點,沒法在開發過程當中被順暢的使用,若是咱們能把重構服務集成進IDE、代碼檢視環節、代碼入庫環節以及驗證發佈環節,就可讓重構工具Build In在可信開發過程當中,讓重構服務觸手可及。
高效:特別是在IDE上的重構分析服務,若是分析過程須要花費太長時間,程序員極可能就不會使用這些重構服務,他們寧肯手工重構。