我常常喜歡把一個系統(tǒng)比喻成一輛車,你需要經常對它做維護和保養(yǎng)债沮,才能保證它的良好運作炼吴。如果不這么做,雖然看著能開疫衩,但某一天一個嚴重的問題就會導致極其危險的后果硅蹦。而持續(xù)重構就是我們給系統(tǒng)做的保養(yǎng),這對于保證系統(tǒng)的穩(wěn)定運行非常關鍵闷煤。
我曾主導過不少系統(tǒng)重構工作童芹,從中也得出了一些我所認為的最佳實踐,希望也能給其他程序員朋友們一些參考鲤拿。
從構建工具開始
在接手去重構一個新的系統(tǒng)時假褪,我常常會發(fā)現他們的構建腳本寫得有多糟糕,有的系統(tǒng)甚至根本沒有使用構建工具近顷。更可氣的是嗜价,負責系統(tǒng)的開發(fā)人員往往并不把它當回事,這就帶來以下一些問題:
- 自動化程度低下:很多本該由構建工具自動完成的工作幕庐,如:編譯久锥,測試,部署异剥,都需要人工干預完成瑟由,或者由于構建腳本寫得不好,本來可以增量編譯和部署的冤寿,變?yōu)槊看涡枰烤幾g和部署歹苦,修改一行代碼只用了一分鐘,而等待構建卻用了多達5分鐘時間督怜。
- 缺少有效的包依賴與版本管控:構建工具可以幫助我們進行有效的依賴包與版本管理殴瘦。缺少了它,則很可能造成因不同開發(fā)人員引入的第三方包版本不一致所導致的系統(tǒng)問題号杠。
- 缺少自動化測試覆蓋:構建工具能夠幫助我們在每次版本構建時蚪腋,執(zhí)行自動化測試丰歌,這對版本的交付是一個非常有效的質量保證。
- 不利于團隊構建:開發(fā)團隊總是具有一定流動性的屉凯,我們經常需要新人加入團隊參與系統(tǒng)的開發(fā)立帖。缺乏良好的構建工具,往往迫使每個新人都需要耗費大量的時間去搭建開發(fā)環(huán)境悠砚,這對團隊的構建也是非常不利的晓勇。
因此,對于我來說灌旧,系統(tǒng)重構的第一步便是引入構建工具或重寫構建腳本:
- 引入構建工具:對于后端Java程序來說绑咱,我最常使用的便是Groovy,目前也開始嘗試使用Gradle枢泰。而對于前端來說Grunt或更新的Gulp都是不錯的選擇描融。
- 第三方包依賴與版本管理:通過定義全局屬性文件的方式,定義如系統(tǒng)的版本號宗苍,名稱等信息,并在構建腳本中明確定義引入第三方包的依賴關系與使用的版本薄榛。使得整個開發(fā)團隊都能使用統(tǒng)一且標準的開發(fā)環(huán)境讳窟。
- 實現自動化:通過定義不同的target(注:Groovy中的一個任務稱為target),來實現不同的目標。比如:增量編譯與本地部署敞恋,自動化測試丽啡,打全量版本包等等∮裁ǎ總之补箍,將一切需要手工完成的任務,利用工具去幫你完成啸蜜。
- 寫入開發(fā)手冊:將構建工具的使用坑雅,不同target針對的不同應用場景寫入開發(fā)手冊,能夠有效地減少新人的學習成本衬横,并使整個團隊開發(fā)效率得到提升裹粤。
讓自動化測試成為重構的保障
我們重構的目的往往是去解決系統(tǒng)的某些痛點,這些痛點也往往是系統(tǒng)的核心功能蜂林,因此遥诉,在你直接動代碼之前,需要詳細分析修改可能造成的關聯影響噪叙。下面是我在做關鍵功能重構時所采用的步驟:
- 詳細Review該功能的需求
- 針對需求矮锈,完善自動化單元測試案例
- 將這部分單元測試的執(zhí)行引入到每次自動化構建中
在大部分我重構過的項目中,起初自動化單元測試都是缺失的睁蕾。由于對核心功能的重構苞笨,往往涉及到大量代碼的反復修改,因此,通過引入單元測試猫缭,可以非常有效地避免因重構造成的關聯影響葱弟。而通過重構完善自動化測試,在我看來也是一個很好的重構實踐猜丹,它將為我們未來的持續(xù)重構打下良好的基礎芝加。
代碼級的持續(xù)重構
雖然我們一開始總是能夠確保代碼的質量,但不可否認射窒,我們的代碼會隨著時間的推移變得越來越糟藏杖。這其中可能包括:
- 重復的代碼,它們可能存在于同一個類或不同類中
- 不一致或沒有標識性的對象脉顿、變量或方法命名
- 過長的代碼段
- 讓人費解的布爾表達式
- 過于復雜的邏輯判斷
- 對象錯誤地暴露其內部狀態(tài)
- 遭廢棄但沒有刪除的類或方法
對于上面這些代碼中的壞味道蝌麸,你應該一有機會就嘗試去重構它。但記住艾疟,你不應該操之過急来吩,想著一下在把所有問題都一起解決。你只需要先識別出這些問題蔽莱,然后分步驟地逐步去解決弟疆,而每次重構你需要充分識別可能造成的關聯影響。如果你已經為你的代碼寫了單元測試盗冷,那你的重構將會有很好的測試保證怠苔。如果沒有,你也可以盡可能找人幫你review修改的代碼仪糖,因為不同的人來看你所修改的代碼總是能發(fā)現不同的東西柑司。
另外,我們現在使用的IDE也能為我們提供很多幫助锅劝,比如找出要重構的方法在哪些地方被調用到攒驰,或者要重構的類的層級關系等等。它還能幫助我們自動地去重命名一個變量故爵、方法甚至是類讼育。
基于微服務的重構
最后,讓我們從架構的角度來看看系統(tǒng)的重構稠集。說到架構奶段,時下最流行的一定是“微服務”架構了。在我看來剥纷,微服務并非是一個全新的架構方法論痹籍,而是對SOA——面向服務架構的一次升級。它的出現源于云計算晦鞋、容器技術蹲缠、DevOps等技術以及全新運維理念的不斷成熟棺克。
最近,我正在主導一個遺留系統(tǒng)基于微服務架構的升級工作线定。在技術層面娜谊,雖然業(yè)界已經出現了多個支持微服務的架構,我們選擇的是Spring Boot斤讥,主要是因為它的背后是Spring團隊強有力的技術支持與維護纱皆。我們的重構也很簡單:
- 服務識別
- UI與服務的剝離
- 構建服務
由于采用微服務架構,我們并不會在原有系統(tǒng)上進行重構芭商,而是創(chuàng)建一個新的基于SpringBoot的項目派草,將原有系統(tǒng)的功能,逐步拆分成一個個服務铛楣,添加到新的項目中近迁,然后利用一些開關設置,將原有功能切換到新的基于微服務的系統(tǒng)中簸州,這是與之前系統(tǒng)重構一個很大的區(qū)別鉴竭。
使用微服務框架,可以使開發(fā)變得更加簡明岸浑,然而搏存,它的難點恰恰在于服務的發(fā)現與定義。你系統(tǒng)中的哪些服務應該被獨立出來助琐,形成對外的服務祭埂,服務的粒度又應該是怎樣的呢面氓?我在做相關架構時兵钮,其實參考了領域驅動設計的思想,先識別出系統(tǒng)所包含的那些領域模型舌界,然后按照領域的劃分與不同的粒度來規(guī)劃系統(tǒng)的服務掘譬。
重構雖然無法直接創(chuàng)造業(yè)務價值,但卻能顯著提高系統(tǒng)的可維護性呻拌,所以你在重構上所投入的每一分鐘葱轩,都將轉變?yōu)槲磥砉?jié)省更多的維護時間,這也是為什么我們需要持續(xù)重構的原因藐握。