重構的道法術器:探討AI智能對工具的影響
經(jīng)過影片出租店的完整演練,對這樣一個如麻雀般完整而小的遺留項目展開重構,使得我們對重構建立了一個整體的印象,也有利于我們將前面介紹的各種重構知識串聯(lián)起來,現(xiàn)在,有必要對整個重構做一次復盤。為了幫助大家更好地理解重構,我認為可以從道、法、術、器這四個層次做一番總結。
圖片
道是萬物變遷循環(huán)中亙古不變的規(guī)律,是自然環(huán)境、事物的自然規(guī)律和發(fā)展方向。道以明向,它決定了重構的方向,也決定了在軟件研發(fā)過程中,什么樣的活動才可以稱之為是重構。我在第3章給出過重構的定義:在不增加任何新功能的情況下,通過運用一系列可控的改進手段對既有代碼做出優(yōu)化,使其變得更容易理解,更容易復用,更容易擴展。這一定義圈定了重構的范圍,主要針對代碼層面,也可以具體稱之為“代碼重構”。
自有重構以來,也陸續(xù)有人創(chuàng)造更多的概念,如數(shù)據(jù)庫重構、網(wǎng)站重構以及架構重構。它們與代碼重構雖然不同,卻遵循相同的道,即在不增加任何新功能且不破壞現(xiàn)有功能的前提下,對目標進行優(yōu)化和改進。違反了這個“道”,就不能說是重構,又或者說沒有達成目標的重構。例如:增加了新功能的任何操作,都不能稱之為重構;破壞了現(xiàn)有功能的重構,就不能稱之為是合格的重構;如果重構之后,目標沒有得到任何優(yōu)化和改善,則不能稱之為是好的重構。對代碼如此,對數(shù)據(jù)庫、網(wǎng)站和架構,都是如此,只是優(yōu)化和改進的目標不同罷了。
本專欄討論的重構內(nèi)容都屬于代碼重構,因此它的優(yōu)化和改進目標就是讓代碼變得“更容易理解,更容易復用,更容易擴展”,即提升代碼的可讀性、可復用性與可擴展性。
法是在探求“道”的過程中經(jīng)過實踐思考、歸納總結出的規(guī)則體系和方法原則。法以立本,是實現(xiàn)重構目標的規(guī)則和方法。Martin Fowler在《重構》一書中總結的重構手法(不含重構手法的具體操作步驟)是“法”的一部分;我在第4章總結的重構三要素,也可以認為是“法”的一部分;如果重構的代碼使用了如Java、C#這樣的面向對象語言,則基本的面向對象設計原則和設計模式,也可以稱之為是“法”的一部分;由于重構需要單元測試做保護,為單元測試規(guī)定的FIRST原則也可以認為是“法”的一部分。
通過前面各章對案例實踐的講解,可以看出這些“法”是正確進行重構的基礎,也是對具體操作的指導。例如,第5章和第6章先后介紹的迪米特法則與信息專家模式,很好地指導了類的職責分配,從而決定采用提取方法和移動方法等重構手法;又例如在第10章提到的“關注點分離”原則,它指導開發(fā)人員學會分辨關注點,將其分離為不同的職責,并采用提取方法、提取委派等重構手法;再例如第23章提到的“差異式編程”,它決定了繼承的設計思路,指導我們在重構時,需要將和PriceCode有關的職責分離到單獨的繼承體系。
至于具體該如何運用這些“法”,就屬于“術”的層次了。術是在規(guī)則體系指導下的具體操作技術,只要“道、法”不變,“術”可千變?nèi)f化。術以立策,如果不通過“術”將抽象的方法和法則轉化為實際操作的過程,代碼重構就無法落地。除了具體的重構步驟之外,第22章提出的“深度優(yōu)先”與“廣度優(yōu)先”的結合策略,多種重構手法結合的策略,如內(nèi)聯(lián)與提取成員之間的配合,這些內(nèi)容都是在“法”的指導下實施的具體方法,屬于“術”的范圍。
器是指有形的物質(zhì)或有形的工具。器以成事,是實現(xiàn)術和法的物質(zhì)基礎。重構的器主要為IDE(包括與重構相關的插件),也包括重構需要用到的各種框架,如JUnit、AssertJ、Mokito,還包括和代碼質(zhì)量有關的工具,如SonarQube。
“器”同樣是變化的,且它的變化更其迅速,隨著技術的不斷進步,它甚至會不斷“吞噬”原本屬于術的范圍,本該由開發(fā)人員具體操作和執(zhí)行的事情,慢慢被“器”所取代。Martin Fowler在剛剛出版《重構》一書之時,只有一款稱為“Refactoring Browser(重構瀏覽器)”的工具,可以對Smalltalk程序實施一些簡單的重構。當時,Martin Fowler總結的許多重構手法(屬于“術”的層次)都需要開發(fā)人員手動完成,以至于他在書中給出了各種重構手法的具體做法。以最常見的“提取方法”重構為例,書中給出的做法為(參見熊節(jié)翻譯的《重構》第一版第111頁,本文有刪減,書中將method翻譯為函數(shù)):
- 創(chuàng)造一個新函數(shù),根據(jù)這個函數(shù)的意圖來對它命名
- 將提煉出的代碼從源函數(shù)復制到新建的目標函數(shù)中
- 仔細檢查提煉出的代碼,看看其中是否引用了“作用域限于源函數(shù)”的變量
- 檢查是否有“僅用于被提煉代碼段”的臨時變量
- ……
- 將被提煉代碼段中需要讀取的局部變量,當做參數(shù)傳給目標函數(shù)
- 處理完所有局部變量之后,進行編譯
- 在源函數(shù)中,將被提煉代碼段替換為對目標函數(shù)的調(diào)用
- 編譯,測試
提取方法屬于“法”的范圍,而具體執(zhí)行提取方法的以上步驟則屬于“術”的范圍。之所以一個簡單的提取方法都需要定義這么多繁瑣的步驟,就是為了執(zhí)行安全的重構。如果沒有工具幫助,就需要開發(fā)人員嚴格地按照這些步驟執(zhí)行??墒?,在本專欄演示“提取方法”重構時,哪有這么麻煩?這是因為這些有規(guī)律可循的重構步驟已經(jīng)被諸如Intellij IDEA提供的重構工具所替代,它幫我們自動完成了對新函數(shù)的創(chuàng)建,對提煉代碼的復制,對提煉代碼段中各種變量的檢查,對提煉代碼的引用等。
如果說這些重構工具只是“器”對“術”的“侵略”,那么AI大模型的發(fā)展則進一步開疆拓土,不僅侵占了“術”的領地,還毫不客氣地開始對“法”領地的侵略。
圖片
由于AI工具或AI智能體(如Cursor、Trae、GitHub Copilot等)擁有了LLM作為“超級大腦”,因而它具有了甚至比人類更強的學習能力。它不僅學會了“術”的知識,還進一步學會了“法”的知識。在后續(xù)文章就可以看到,只要在提示詞中明確要求AI工具按照迪米特法則或信息專家模式對遺留代碼進行重構,它就知道該選擇提取方法和移動方法等重構手法,智能地完成代碼的重構。
由于AI大模型為重構工具注入了“智力”,使得過去的自動重構升級為智能重構,也讓重構的操作體驗,從過去的菜單操作、鼠標操作與快捷鍵操作(即所謂的GUI),變革為以自然語言為載體的聊天式操作(即所謂的LUI)。
AI工具引發(fā)的是軟件研發(fā)整個生態(tài)全方位的變革,代碼重構必然也會受到這一輪沖擊波的影響。但以目前的AI智能水平,它還不能完全替代一名重構專家,特別在面對一個稍顯復雜的遺留代碼時,假如操作人員只是向AI工具發(fā)出一個簡單的指示:“請重構這段代碼!”那么,它就只能完成一些基本的重構操作,如重命名以提升代碼可讀性,提取方法以明確清晰的職責。要執(zhí)行更加復雜的重構,還需要開發(fā)人員向AI工具發(fā)出更加明確的指示,才能更漂亮地完成重構。
如果開發(fā)人員不具備重構的知識,尤其不具備“道”和“法”的知識,就可能難以給出清晰的重構方向與操作指引,此時,AI工具就可能變成一個不斷制造麻煩或拒不配合的團隊成員,加上大模型還存在諸如AI幻覺之類的問題,有時也會做出讓人啼笑皆非的重構操作,這時候,就需要開發(fā)人員運用自己扎實的重構功底,為整個過程指引方向,保駕護航。