程式碼重構,真的只有複雜化一條路嗎?
本文分享自華為雲社群《難道一開始就要把程式碼設計做得複雜嗎?》,作者: JavaEdge 。
看著自己每次根據設計原則及模式的程式碼重構,雖然效果還不錯,但你肯定也自省過:如果我的每段程式碼都這麼寫,是不是過度設計了?把握設計的度,確需長久錘鍊。行業也總結了很多原則,幫助我們把握設計的度。它們是一種思考方法、一種行為準則。
KISS
Keep it simple, stupid,保持簡單、愚蠢。 提醒我們大多數系統,與其變得複雜,保持簡單能讓系統執行更好。
越資深的人,越覺得這大有道理。因為大佬們見識過因為複雜而引發的各種問題。 堆太多功能,調整起來就很費勁。具體到一些場景:
- 有現成庫,就不自己寫
- 能用文字做協議,就別用二進位制
- 方法越短小精悍越好
- 能把一個基本流程打通,軟體就能釋出,無需那麼多功能(MVP)
看起來很接地氣啊!真是吸引crud boy呢,但無法指導具體的工作。因為,啥叫保持簡單,怎麼就叫複雜?這都沒有標準。
所以,有人基於自己的理解給了具體原則:
YAGNI
You aren’t gonna need it,你用不著它。如非必要,勿增功能。
軟體設計對抗的是需求規模:
- 通過努力,讓軟體在需求規模膨脹之後,依然能平穩發展
- 努力控制需求規模
很多需求不需要做。很多產品經理以為很重要的功能實際上是沒什麼用的。真正重要的功能大約只佔20%,80%的功能可能大多數人都用不到。做了更多的功能,並不會得到更多的回報,但是,做了更多的功能,軟體本身卻會不斷地膨脹,越發難以維護。
所以,在現實經常看到一些功能簡單的東西不斷湧現,去顛覆更復雜的東西。比如,雖然Word已經很強大了,但對於很多人而言,它還只是一個寫字的工具,甚至它的重點排版功能都用得非常少。而Markdown簡單地讓我們專注寫內容,而且簡單的幾個排版標記在日常溝通中就完全夠用了。
儘量可能不去做不該做的事,從源頭堵住問題吧!
DRY
Don’t repeat yourself,不要重複自己。在一個系統中,每一處知識都必須有單一、明確、權威地表述。Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
最簡單理解:“不要做cv工程師”。這還遠遠不夠,DRY針對的是你對知識和意圖的複製:在兩個不同地方的兩樣東西表達形式不同,但表達內容卻可能相同。
如下列印賬戶資訊,這種寫法肯定很常見:
``` public void printBalance(final Account account) { System.out.printf("Debits: %10.2f\n", account.getDebits()); System.out.printf("Credits: %10.2f\n", account.getCredits()); if (account.getFees() < 0) { System.out.printf("Fees: %10.2f-\n", -account.getFees()); } else { System.out.printf("Fees: %10.2f\n", account.getFees()); }
System.out.printf(" ----\n");
if (account.getBalance() < 0) { System.out.printf("Balance: %10.2f-\n", -account.getBalance()); } else { System.out.printf("Balance: %10.2f\n", account.getBalance()); } } ```
這段隱藏一些重複。比如,對負數的處理顯然是複製的,可通過增加一個方法消除:
``` String formatValue(final double value) { String result = String.format("%10.2f", Math.abs(value)); if (value < 0) { return result + "-"; } else { return result + " "; } }
void printBalance(final Account account) { System.out.printf("Debits: %10.2f\n", account.getDebits()); System.out.printf("Credits: %10.2f\n", account.getCredits()); System.out.printf("Fees:%s\n", formatValue(account.getFees())); System.out.printf(" ----\n"); System.out.printf("Balance:%s\n", formatValue(account.getBalance())); } ```
數字欄位格式反覆出現,不過,格式與我們抽取出來的方法是一致的,所以,可以複用一下:
``` String formatValue(final double value) { String result = String.format("%10.2f", Math.abs(value)); if (value < 0) { return result + "-"; } else { return result + " "; } }
void printBalance(final Account account) { System.out.printf("Debits: %s\n", formatValue(account.getDebits())); System.out.printf("Credits: %s\n", formatValue(account.getCredits())); System.out.printf("Fees:%s\n", formatValue(account.getFees())); System.out.printf(" ----\n"); System.out.printf("Balance:%s\n", formatValue(account.getBalance())); } ```
列印格式其實也重複,如果我要在標籤和金額之間加一個空格,相關的程式碼都要改,所以,這也是一個可以消除的重複:
``` String formatValue(final double value) { String result = String.format("%10.2f", Math.abs(value)); if (value < 0) { return result + "-"; } else { return result + " "; } }
void printLine(final String label, final String value) { System.out.printf("%-9s%s\n", label, value); }
void reportLine(final String label, final double value) { printLine(label + ":", formatValue(value)); }
void printBalance(final Account account) { reportLine("Debits", account.getDebits()); reportLine("Credits", account.getCredits()); reportLine("Fees", account.getFees()); System.out.printf(" ----\n"); reportLine("Balance", account.getBalance()); } ```
重構後:
- 改金額列印格式,就去改formatValue
- 改標籤格式,就去改reportLine
有人說這種調整粒度太小。如你這樣感覺,證明你看問題的粒度太大。品味這個修改,與分離關注點和單一職責原則異曲同工:粒度要小。
DRY不侷限於寫程式碼:
- 註釋和程式碼之間存在重複,可以嘗試把程式碼寫得更清晰
- 內部API在不同的使用者之間存在重複,可以通過中立格式進行API的定義,然後用工具生成文件、模擬 API 等等
- 開發人員之間做的事情存在重複,可以建立溝通機制降低重複; ……
都是在試圖減少重複,其實也是減少了維護成本。
簡單設計
Simple Design,提出者Kent Beck,只包含如下規則,後3條規則是重構方向
1 通過所有測試
保證系統能夠按照預期工作。怎麼知道系統按照預期工作,就需要有配套自動化測試,最好能TDD,最根本的還是要懂設計,否則,你的程式碼就是不可測。
2 消除重複
正如DRY原則所說,你得能發現重複,就需要你對分離關注點有理解
3 表達出程式設計師的意圖
編寫有表達性的程式碼,這也需要你對“什麼是有表達性的程式碼”有認識。程式碼要說明做什麼,而不是怎麼做
4 讓類和方法的數量最小化
讓類和方法的數量最小化,則告訴我們不要過度設計,除非你已經看到這個地方必須要做一個設計,比如,留下適當的擴充套件點,否則,就不要做。
能做出過度設計的前提,是已經懂得了各種設計,這時才需要用簡單設計的標準對自己進行約束。所以,所謂簡單設計,對大多數人並不“簡單”。
沒有良好設計,程式碼就沒有可測試的介面,根本沒有辦法測試,TDD也就無從談起。不懂設計,重構就只是簡單提取方法,改改名字,對程式碼的改進也是相當有限的。
當然了,簡單設計的前提是,把程式設計基礎打牢。片面地追求敏捷實踐,而忽視基本功,是捨本逐末。
- 帶你掌握 C 中三種類成員初始化方式
- 實踐GoF的設計模式:工廠方法模式
- DCM:一個能夠改善所有應用資料互動場景的中介軟體新秀
- 手繪圖解java類載入原理
- 關於加密通道規範,你真正用的是TLS,而非SSL
- 程式碼重構,真的只有複雜化一條路嗎?
- 解讀分散式排程平臺Airflow在華為雲MRS中的實踐
- 透過例項demo帶你認識gRPC
- 帶你聚焦GaussDB(DWS)儲存時遊標使用
- 傳統到敏捷的轉型中,誰更適合做Scrum Master?
- 輕鬆解決研發知識管理難題
- Java中觀察者模式與委託,還在傻傻分不清
- 如何使用Python實現影象融合及加法運算?
- 什麼是強化學習?
- 探索開源工作流引擎Azkaban在MRS中的實踐
- GaussDB(DWS) NOT IN優化技術解密:排他分析場景400倍效能提升
- Java中觀察者模式與委託,還在傻傻分不清
- Java中的執行緒到底有哪些安全策略
- 一圖詳解java-class類檔案原理
- Java中的執行緒到底有哪些安全策略