官术网_书友最值得收藏!

1.4 重構(gòu)為Kotlin

在開(kāi)啟Kotlin之旅時(shí),我們正負(fù)責(zé)維護(hù)和增強(qiáng)關(guān)鍵業(yè)務(wù)系統(tǒng),不能只專注于將Java代碼庫(kù)轉(zhuǎn)換為Kotlin。我們總是不得不在滿足新業(yè)務(wù)需求的同時(shí)將代碼遷移到Kotlin,并在這樣做的同時(shí)維護(hù)一個(gè)混合的Java/Kotlin代碼庫(kù)。我們通過(guò)小步修改來(lái)控制風(fēng)險(xiǎn),使每個(gè)修改都易于理解,并且在發(fā)現(xiàn)它破壞了某些東西時(shí)可以低成本地拋棄。在這個(gè)過(guò)程中,我們首先將Java代碼轉(zhuǎn)換為Kotlin,這為我們提供了Kotlin語(yǔ)法的類Java設(shè)計(jì)。然后,我們逐步應(yīng)用Kotlin的語(yǔ)言特性,使代碼越來(lái)越易于理解,類型更安全、更簡(jiǎn)潔,并且具有更多的組合結(jié)構(gòu),更容易改變,不會(huì)出現(xiàn)令人不快的意外。

細(xì)微的、安全的、可逆的改變都會(huì)改善設(shè)計(jì),以此將地道的Java代碼重構(gòu)為地道的Kotlin代碼。

不同語(yǔ)言之間進(jìn)行重構(gòu)通常比單一語(yǔ)言中進(jìn)行重構(gòu)更難,因?yàn)橹貥?gòu)工具無(wú)法很好地在跨語(yǔ)言邊界的情況下工作。將邏輯從一門語(yǔ)言移植到另一門語(yǔ)言必須手動(dòng)完成,這需要更長(zhǎng)的時(shí)間并會(huì)引入更多的風(fēng)險(xiǎn)。一旦使用了多門語(yǔ)言,語(yǔ)言的邊界就會(huì)阻礙重構(gòu),因?yàn)楫?dāng)你重構(gòu)一門語(yǔ)言的代碼時(shí),IDE不會(huì)更新那些用其他語(yǔ)言編寫的依賴代碼以使其兼容。

Java和Kotlin的組合之所以獨(dú)特,是因?yàn)檫@兩種語(yǔ)言之間的(相對(duì))無(wú)縫邊界。得益于Kotlin語(yǔ)言的設(shè)計(jì)、映射到JVM平臺(tái)的方式,以及JetBrains在開(kāi)發(fā)者工具上的投資,可以將Java重構(gòu)為Kotlin,以及重構(gòu)一個(gè)混合的Java/Kotlin代碼庫(kù),這幾乎與重構(gòu)一個(gè)單語(yǔ)言的代碼庫(kù)一樣簡(jiǎn)單。

我們的經(jīng)驗(yàn)是,可以在不影響生產(chǎn)力的情況下將Java重構(gòu)為Kotlin,而且隨著我們將更多的代碼庫(kù)轉(zhuǎn)換為Kotlin,生產(chǎn)力也會(huì)隨之加快。

1.4.1 重構(gòu)的原則

自Martin Fowler于1999年出版《重構(gòu):改善既有代碼的設(shè)計(jì)》(Addison-Wesley)一書(shū)以來(lái),重構(gòu)的實(shí)踐已經(jīng)有了長(zhǎng)足的發(fā)展。這本書(shū)在當(dāng)時(shí)還不得不詳細(xì)地說(shuō)明重構(gòu)的手動(dòng)步驟,即使是簡(jiǎn)單的重構(gòu),如重命名標(biāo)識(shí)符。該書(shū)還指出,一些先進(jìn)的開(kāi)發(fā)環(huán)境已經(jīng)開(kāi)始提供對(duì)重構(gòu)的自動(dòng)化支持,以減少這種煩瑣的工作。現(xiàn)如今,我們期望我們的工具能夠自動(dòng)處理更加復(fù)雜的情況,如提取接口或更改函數(shù)簽名。

這些單個(gè)的重構(gòu)很少是單獨(dú)存在的,既然(代碼)構(gòu)建塊的重構(gòu)可以自動(dòng)執(zhí)行,我們就有時(shí)間和精力將它們組合起來(lái),對(duì)代碼庫(kù)進(jìn)行更大規(guī)模的修改。當(dāng)IDE沒(méi)有為我們期望進(jìn)行的大規(guī)模改造提供專門的用戶界面操作時(shí),我們必須將其拆解為一連串更細(xì)化的重構(gòu)步驟來(lái)執(zhí)行。盡可能地使用IDE的自動(dòng)重構(gòu)功能,而當(dāng)IDE不能自動(dòng)完成所需要的轉(zhuǎn)換時(shí),我們就會(huì)退回到文本編輯。

通過(guò)文本編輯進(jìn)行重構(gòu)是很乏味的,而且容易出錯(cuò)。為了減少風(fēng)險(xiǎn)和避免無(wú)聊的工作,我們盡量減少必須進(jìn)行的文本編輯的次數(shù)。即便必須要做,我們也希望只編輯單個(gè)表達(dá)式。因此,我們先使用自動(dòng)重構(gòu)方法來(lái)轉(zhuǎn)換代碼,以便只編輯一個(gè)表達(dá)式,然后使用自動(dòng)重構(gòu)方法有序地向著我們所期望的最終狀態(tài)前進(jìn)。

在首次描述這種大規(guī)模重構(gòu)時(shí),我們會(huì)一步一步地進(jìn)行說(shuō)明,并展示每一步的代碼變化。這會(huì)占用相當(dāng)多的篇幅,并且你需要花費(fèi)一些時(shí)間來(lái)閱讀和理解。然而,在實(shí)踐中,應(yīng)用這些大型重構(gòu)是很快的。它們通常只需要幾秒,最多也就幾分鐘。

我們預(yù)計(jì)隨著工具的改進(jìn),本書(shū)中所發(fā)布的重構(gòu)將會(huì)很快過(guò)時(shí)。個(gè)別的重構(gòu)步驟可能會(huì)被重新命名,一些(重構(gòu))組合可能會(huì)作為單獨(dú)的重構(gòu)來(lái)實(shí)現(xiàn)。在你的環(huán)境中進(jìn)行實(shí)驗(yàn),如果能找到能夠逐步安全地改造代碼的更好方法,那么請(qǐng)分享出來(lái)。

1.4.2 良好的測(cè)試覆蓋率是前提

正如Martin Fowler在《重構(gòu):改善既有代碼的設(shè)計(jì)》中所說(shuō):“如果你想重構(gòu),最重要的先決條件是進(jìn)行可靠的測(cè)試。”良好的測(cè)試覆蓋率確保我們的代碼改動(dòng)只是改進(jìn)了設(shè)計(jì),不會(huì)意外地改變系統(tǒng)的行為。在本書(shū)中,假設(shè)已有良好的測(cè)試覆蓋率這個(gè)前提。本書(shū)不涉及如何編寫自動(dòng)化測(cè)試。其他作者已經(jīng)更詳細(xì)地討論了這些主題,例如Kent Beck的Test-Driven Development By Example(Addison-Wesley)、Steve Freeman與Nat Pryce的Growing Object-Oriented Software Guided By Tests(Addison-Wesley)。盡管如此,我們?nèi)匀徽故玖巳绾问褂肒otlin特性來(lái)改進(jìn)測(cè)試。

在一系列的代碼轉(zhuǎn)換過(guò)程中,我們并未說(shuō)明什么時(shí)候該運(yùn)行測(cè)試,而是假定在每一次展示代碼變化之后,都應(yīng)在編譯之后運(yùn)行測(cè)試,無(wú)論變化有多么小。

如果你的系統(tǒng)還沒(méi)有很好的測(cè)試覆蓋率,則在代碼中添加測(cè)試可能會(huì)很困難(而且很昂貴),因?yàn)槟阋獪y(cè)試的邏輯與系統(tǒng)的其他方面糾纏在一起。你陷入了一個(gè)先有雞還是先有蛋的境地:必須重構(gòu)代碼才能增加測(cè)試,以便安全地重構(gòu)。同樣,其他作者已經(jīng)更詳細(xì)地討論了這些話題,例如Michael Feathers的Working Effectively with Legacy Code。我們?cè)趨⒖紩?shū)目中列出了更多關(guān)于這些主題的書(shū)籍。

1.4.3 提交時(shí)考慮git bisect

就像我們沒(méi)有明確說(shuō)明何時(shí)運(yùn)行測(cè)試一樣,我們也不會(huì)明確說(shuō)明何時(shí)提交修改,而是假定只要修改給代碼增加了價(jià)值就會(huì)提交,無(wú)論修改是多么細(xì)微。

我們知道我們的測(cè)試套件并不完美。如果不小心破壞了一些沒(méi)有被測(cè)試覆蓋的代碼,我們希望能盡快找到引入錯(cuò)誤的提交并進(jìn)行修復(fù)。

git bisect命令支持自動(dòng)搜索,我們可以寫一個(gè)新的測(cè)試來(lái)描述錯(cuò)誤場(chǎng)景,然后通過(guò)git bisect對(duì)提交歷史進(jìn)行二分搜索,找出導(dǎo)致該測(cè)試失敗的第一個(gè)提交。

如果歷史記錄中的提交很多,而且包含一些不相關(guān)的改動(dòng),那么git bisect就幫不上什么了,它不能告訴我們哪個(gè)提交中的源碼變化導(dǎo)致了錯(cuò)誤。如果在提交中混合了重構(gòu)和系統(tǒng)行為的改變,則回退不良的重構(gòu)步驟很可能會(huì)破壞系統(tǒng)中的其他行為。

因此,我們會(huì)提交一些小而集中的修改,將重構(gòu)與系統(tǒng)行為的修改分開(kāi),以便了解哪些部分發(fā)生了變化,并修復(fù)錯(cuò)誤的變更。出于同樣的原因,我們很少積壓提交。

我們更喜歡直接將修改提交到主干分支上——“基于主干的開(kāi)發(fā)”——當(dāng)在多個(gè)分支中工作且合并頻率不是那么高時(shí),以一連串小而獨(dú)立的提交來(lái)改變代碼也同樣有益。

主站蜘蛛池模板: 榆林市| 福州市| 女性| 乌拉特前旗| 故城县| 安福县| 田林县| 山丹县| 乐都县| 天镇县| 万年县| 郸城县| 应城市| 平邑县| 浦江县| 周宁县| 万载县| 清苑县| 会理县| 高唐县| 天津市| 水富县| 巨野县| 长垣县| 丰原市| 焦作市| 怀仁县| 喜德县| 阳高县| 商丘市| 正安县| 石嘴山市| 盐边县| 左权县| 宁阳县| 安西县| 永康市| 蕉岭县| 宁德市| 古浪县| 承德市|