譯者 | 劉汪洋
審校 | 重樓
Java 代碼重構(gòu)是一種在不影響代碼外部行為的前提下進(jìn)行的代碼優(yōu)化,它通過(guò)漸進(jìn)和小規(guī)模的優(yōu)化來(lái)改善現(xiàn)有代碼的結(jié)構(gòu)和質(zhì)量。重構(gòu)的目標(biāo)是提高代碼的可讀性、性能、可維護(hù)性和效率等。
Martin Fowler 是這個(gè)領(lǐng)域的權(quán)威的大牛和非常高產(chǎn)的作家,他在多篇文章和書(shū)籍中探討了代碼設(shè)計(jì)和重構(gòu)的主題。在他的作品《重構(gòu):改善既有代碼的設(shè)計(jì)》中,他精辟地解釋了重構(gòu)的本質(zhì):
_“重構(gòu)是在不改變代碼外在行為的前提下,對(duì)代碼做出修改,以改進(jìn)程序內(nèi)在結(jié)構(gòu)的過(guò)程。重構(gòu)是一種經(jīng)過(guò)千錘百煉形成的有條不紊的程序整理方法,可以最大限度地減少整理過(guò)程中引入的錯(cuò)誤幾率。其核心是不斷進(jìn)行一些小的優(yōu)化,每個(gè)優(yōu)化看似不起眼,但積少成多,效果顯著。” ——Martin Fowler
在進(jìn)行 Java 代碼重構(gòu)時(shí),可以考慮以下常見(jiàn)的優(yōu)化措施:
為變量和方法選擇具有代表性的名稱(chēng)是增強(qiáng)代碼可讀性的重要方法。
代碼的可讀性是構(gòu)建高質(zhì)量代碼庫(kù)的關(guān)鍵要素之一。易讀的代碼能夠清晰地表達(dá)其目的,而難以理解的代碼則會(huì)增加重構(gòu)過(guò)程中出現(xiàn)錯(cuò)誤的風(fēng)險(xiǎn)。采用有意義的變量和方法名稱(chēng)可以減少注釋的需求,并降低溝通成本。
// 重構(gòu)前int d = 30; // 天數(shù)int h = 24; // 一天的小時(shí)數(shù)// 重構(gòu)后int daysInMonth = 30;int hoursInDay = 24;
在 Java 代碼重構(gòu)技術(shù)中,方法的提取是一種常見(jiàn)而實(shí)用的策略。當(dāng)一個(gè)方法變得過(guò)長(zhǎng)和過(guò)于復(fù)雜時(shí),通過(guò)提取部分功能到一個(gè)新的方法中能夠使原方法更簡(jiǎn)潔和易讀。這不僅使代碼更具可維護(hù)性,還提高了其可重用性。
假設(shè)你有一個(gè)簡(jiǎn)單的類(lèi),用于處理訂單并計(jì)算小計(jì)、稅費(fèi)和總費(fèi)用。
public class OrderProcessor { private List<Item> items; private double taxRate; public double processOrder() { double subtotal = 0; for (Item item : items) { subtotal += item.getPrice(); } double totalTax = subtotal * taxRate; double totalCost = subtotal + totalTax; return totalCost; }}
你可以將該代碼重構(gòu),把計(jì)算小計(jì)、稅費(fèi)和總費(fèi)用的代碼分別提取到 calculateSubtotal、calculateTax 和 calculateTotalCost 三個(gè)獨(dú)立的方法中,從而使類(lèi)更加易讀、模塊化和可重用。
public class OrderProcessor { private List<Item> items; private double taxRate; public double processOrder() { double subtotal = calculateSubtotal(); double totalTax = calculateTax(subtotal); return calculateTotalCost(subtotal, totalTax); } private double calculateSubtotal() { double subtotal = 0; for (Item item : items) { subtotal += item.getPrice(); } return subtotal; } private double calculateTax(double subtotal) { return subtotal * taxRate; } private double calculateTotalCost(double subtotal, double totalTax) { return subtotal + totalTax; }}
“魔法”數(shù)字和字符串是指直接硬編碼在代碼中的值。這種做法不僅會(huì)降低代碼的可維護(hù)性,還可能因輸入錯(cuò)誤而導(dǎo)致結(jié)果不一致和錯(cuò)誤的增多。為了避免這樣的問(wèn)題,你應(yīng)當(dāng)避免使用硬編碼的值,而是通過(guò)使用具有清晰描述性的常量來(lái)重構(gòu)你的代碼。
// 重構(gòu)前if (status == 1) { // ... 活躍狀態(tài)的代碼 ...}// 重構(gòu)后public static final int ACTIVE_STATUS = 1;if (status == ACTIVE_STATUS) { // ... 活躍狀態(tài)的代碼 ...}
代碼復(fù)用是指刪除代碼庫(kù)中多處出現(xiàn)的重復(fù)或相似的代碼段。這樣的代碼不僅降低了代碼質(zhì)量和效率,還可能導(dǎo)致 bug 更加頻繁的出現(xiàn)和代碼庫(kù)變得更加復(fù)雜。因此,開(kāi)發(fā)人員通常會(huì)對(duì)這類(lèi)代碼感到反感。為了優(yōu)化代碼,我們可以考慮提取重復(fù)部分來(lái)創(chuàng)建可復(fù)用的方法或函數(shù),同時(shí)確保重構(gòu)過(guò)程中保持原有代碼的功能和邏輯。
public class NumberProcessor { // 計(jì)算總和 public int calculateTotal(int[] numbers) { int total = 0; for (int i = 0; i < numbers.length; i++) { total += numbers[i]; } return total; } // 計(jì)算平均值 public double calculateAverage(int[] numbers) { int total = 0; for (int i = 0; i < numbers.length; i++) { total += numbers[i]; } double average = (double) total / numbers.length; return average; }}
public class NumberProcessor { // 計(jì)算總和 public int calculateSum(int[] numbers) { int total = 0; for (int i = 0; i < numbers.length; i++) { total += numbers[i]; } return total; } // 計(jì)算總和 public int calculateTotal(int[] numbers) { return calculateSum(numbers); } // 計(jì)算平均值 public double calculateAverage(int[] numbers) { int total = calculateSum(numbers); double average = (double) total / numbers.length; return average; }}
在優(yōu)化后的代碼中,我們將用于計(jì)算數(shù)組總和的邏輯提取到了一個(gè)名為 calculateSum 的獨(dú)立方法中。現(xiàn)在,calculateTotal 和 calculateAverage 方法可以直接調(diào)用 calculateSum 來(lái)獲取數(shù)組的總和,從而避免了代碼重復(fù)。
隨著時(shí)間的推移,隨著維護(hù)代碼的人越來(lái)越多,代碼庫(kù)容易變得陳舊和混亂。為保證代碼的清晰度和易維護(hù)性,就非常有必要對(duì)代碼進(jìn)行重構(gòu),使其更易理解、維護(hù)和擴(kuò)展。
在簡(jiǎn)化方法的過(guò)程中,首先要識(shí)別出那些包含復(fù)雜嵌套邏輯和承擔(dān)過(guò)多職責(zé)的方法。接著,可以通過(guò)以下幾步來(lái)簡(jiǎn)化它們:
接下來(lái),我們將通過(guò)一個(gè) Java 代碼重構(gòu)示例來(lái)具體展示如何簡(jiǎn)化方法。
簡(jiǎn)化前
public class ShoppingCart { private List<Item> items; // 計(jì)算總價(jià) public double calculateTotalCost() { double total = 0; for (Item item : items) { if (item.isDiscounted()) { total += item.getPrice() * 0.8; } else { total += item.getPrice(); } } if (total > 100) { total -= 10; } return total; }}
我們可以通過(guò)提取 calculateItemPrice 邏輯到 calculateItemPrice 和 applyDiscount 方法中,并使用三元運(yùn)算符來(lái)簡(jiǎn)化條件判斷,使上述示例更為簡(jiǎn)潔。
簡(jiǎn)化后
public class ShoppingCart { private List<Item> items; // 計(jì)算總價(jià) public double calculateTotalCost() { double total = 0; for (Item item : items) { total += calculateItemPrice(item); } total -= applyDiscount(total); return total; } // 計(jì)算價(jià)格 private double calculateItemPrice(Item item) { return item.isDiscounted() ? item.getPrice() * 0.8 : item.getPrice(); } // 獲取滿減 private double applyDiscount(double total) { return total > 100 ? 10 : 0; }}
圖片來(lái)源: Codecademy
紅綠重構(gòu),又稱(chēng)測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(TDD),是一種強(qiáng)調(diào)先編寫(xiě)測(cè)試再編寫(xiě)能通過(guò)這些測(cè)試的代碼的代碼重構(gòu)技術(shù)。該技術(shù)是一個(gè)循環(huán)迭代的過(guò)程,每一輪迭代都包括編寫(xiě)新的測(cè)試和足夠的代碼來(lái)通過(guò)這些測(cè)試,最后對(duì)代碼進(jìn)行重構(gòu)。
這一技術(shù)包括以下三個(gè)階段:
每完成一個(gè)測(cè)試用例后,你將進(jìn)入下一個(gè)循環(huán),繼續(xù)編寫(xiě)新的測(cè)試用例和對(duì)應(yīng)的代碼,然后再進(jìn)行代碼重構(gòu)以實(shí)現(xiàn)更好的優(yōu)化。
可能你已對(duì)面向?qū)ο缶幊讨械?SOLID 原則有所了解。SOLID 是五個(gè)設(shè)計(jì)原則的首字母縮寫(xiě)。
單一責(zé)任原則是首要原則,該原則強(qiáng)調(diào)每個(gè)類(lèi)應(yīng)僅有一個(gè)變化的原因,即一個(gè)類(lèi)只負(fù)責(zé)一個(gè)功能點(diǎn)遵守單一責(zé)任原則是確保代碼可維護(hù)、可讀、靈活和模塊化的基本方式之一。下面我們將展示一個(gè) OrderProcessor 類(lèi)的示例,這個(gè)類(lèi)違反了單一責(zé)任原則,因?yàn)樗瑫r(shí)承擔(dān)了訂單處理和記錄信息日志兩項(xiàng)職責(zé)。
public class OrderProcessor { // 處理訂單 public void processOrder(Order order) { // 訂單驗(yàn)證 // 處理邏輯 // 日志記錄 Logger logger = new Logger(); logger.log("Order processed: " + order.getId()); }}
為了遵循單一責(zé)任原則,我們可以將該類(lèi)重構(gòu)為三個(gè)類(lèi):一個(gè)是 OrderProcessor 類(lèi),只負(fù)責(zé)訂單處理;OrderValidator負(fù)責(zé)訂單校驗(yàn),另外一個(gè)是 OrderLogger 類(lèi),專(zhuān)門(mén)負(fù)責(zé)日志記錄。
public class OrderProcessor { private final OrderValidator orderValidator; public OrderProcessor(OrderValidator orderValidator) { this.orderValidator = orderValidator; } // 處理訂單 public void processOrder(Order order) { // 訂單驗(yàn)證 if(!orderValidator.validate(order)) { throw new IllegalArgumentException("Order is not valid"); } // 處理邏輯 // ... // 日志記錄 OrderLogger logger = new OrderLogger(); logger.logOrderProcessed(order); }}public class OrderValidator { // 訂單驗(yàn)證 public boolean validate(Order order) { // 驗(yàn)證邏輯 // ... return true; }}public class OrderLogger { public void logOrderProcessed(Order order) { Logger logger = new Logger(); logger.log("Order processed: " + order.getId()); }}
代碼重構(gòu)是一個(gè)能夠顯著提升代碼質(zhì)量的重要步驟,它帶來(lái)了我們之前強(qiáng)調(diào)的諸多好處。但在進(jìn)行重構(gòu)時(shí)也需謹(jǐn)慎,特別是在處理龐大的代碼庫(kù)或不熟悉的代碼庫(kù)時(shí),以避免無(wú)意中改變軟件的功能或產(chǎn)生未預(yù)見(jiàn)的問(wèn)題。
為了避免任何潛在問(wèn)題,您可以參考以下重構(gòu) Java 代碼的技巧和最佳實(shí)踐:
重構(gòu)是一項(xiàng)至關(guān)重要的技術(shù)實(shí)踐,它是確保軟件項(xiàng)目長(zhǎng)期成功的關(guān)鍵。 通過(guò)融入我們之前討論的技術(shù)到你的開(kāi)發(fā)周期并嚴(yán)格遵循最佳實(shí)踐,你可以把任何復(fù)雜且混亂的代碼庫(kù)改造為一個(gè)可讀、可維護(hù)和可擴(kuò)展的軟件解決方案。但請(qǐng)注意,Java 代碼重構(gòu)不是一次性的任務(wù),而是一個(gè)可以持續(xù)整合到你的開(kāi)發(fā)周期中的過(guò)程。
在軟件開(kāi)發(fā)的任何階段都可以進(jìn)行代碼重構(gòu)。無(wú)論是在添加新功能、修復(fù) bug 還是優(yōu)化難以理解的代碼片段時(shí),都是進(jìn)行重構(gòu)的好時(shí)機(jī)。定期預(yù)留時(shí)間來(lái)進(jìn)行重構(gòu)可以避免技術(shù)債務(wù)的累積,從而維持代碼庫(kù)的高質(zhì)量。
你可以從識(shí)別代碼庫(kù)中難以理解、存在重復(fù)邏輯或容易產(chǎn)生 bug 的區(qū)域開(kāi)始。尋找具有冗長(zhǎng)的方法、復(fù)雜條件語(yǔ)句的代碼,并嘗試遵循“單一職責(zé)原則”來(lái)提高代碼的組織性。
擁有一套完善的自動(dòng)化測(cè)試套件是減少重構(gòu)過(guò)程中引入 bug 的風(fēng)險(xiǎn)的關(guān)鍵。在開(kāi)始重構(gòu)之前,確保你的代碼具有良好的測(cè)試覆蓋率,這將幫助你捕捉任何可能的回歸問(wèn)題,確保代碼功能的穩(wěn)定性。
首先,你需要確定目標(biāo)類(lèi)并深入理解其行為和依賴(lài)關(guān)系。然后可以考慮拆分龐大的方法和將方法移動(dòng)到更適合的類(lèi)中,同時(shí)利用繼承和接口來(lái)實(shí)現(xiàn)更清晰的結(jié)構(gòu)。此外,重命名變量和方法、重新組織代碼結(jié)構(gòu)和簡(jiǎn)化條件語(yǔ)句都可以提高代碼的可讀性。最后,確保對(duì)所有更改進(jìn)行徹底測(cè)試,以保證功能的完整性。
劉汪洋,51CTO社區(qū)編輯,昵稱(chēng):明明如月,一個(gè)擁有 5 年開(kāi)發(fā)經(jīng)驗(yàn)的某大廠高級(jí) Java 工程師,擁有多個(gè)主流技術(shù)博客平臺(tái)博客專(zhuān)家稱(chēng)號(hào)。
標(biāo)題:CODE REFACTORING IN JAVA: TIPS, BEST PRACTICES, TECHNIQUES,作者:Digma
本文鏈接:http://www.tebozhan.com/showinfo-26-14123-0.htmlJava中的代碼重構(gòu):技巧、優(yōu)秀實(shí)踐與方法
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com