合格的程序員不僅僅是讓代碼跑起來,而是要做到代碼整潔,只滿足為了能讓編譯器通過編譯,機器能跑就行而寫代碼的程序會算不上開發者,碼農都不算。
好的命名能體現出代碼的特征,含義或者是用途,讓閱讀者可以根據名稱的含義快速厘清程序的脈絡。
本篇分享如下代碼命名套路來提高我們代碼命名:
- 勿模糊,準確達意
- 避免誤導
- 做有意義的區分
- 結合上下文簡化名稱
- 使用可搜索、易讀的名稱
- 包命名規范
- 類名與方法名規范
混亂的代價
我相信每個程序員都被某些人的垃圾代碼惡心過,導致開發進度被嚴重延緩、性能差勁、bug 多。
每次新增和修改代碼如履薄冰,我們只有對那堆腐朽的代碼了然于胸才敢修改。
隨著時間推移,團隊生產力下降,所有人都抵觸這個項目,對其束手無策。
新手不熟悉原來的場景和設計,不知道如何修改才符合實際意圖,導致更容易出現混亂。
最后,開發團隊產生了抵觸心理并造反了,再也無法忍受在這個垃圾代碼基礎上做開發,而管理層不愿意投入資源重新設計。
一個優秀的開發者應該時刻保持代碼整潔,無關 deadline。
為什么會寫出垃圾代碼呢?
有的人可能會說,需求變化違背了最初的設計、排期太緊沒法干好......
其實,這是一種不專業的托詞。
推進進度是產品經理他們該干的,雖然癡迷于進度,但是多數產品經理也會期望有良好的可拓展代碼以便應對市場變換莫測的需求。
連海誓山盟的愛情都會變,又如何做到需求不會改變呢?
所以我們比他們更加重視代碼質量,才能應對變化的需求。
保護代碼持續整潔優雅是每個優秀開發者都應該遵守的原則。
混亂的代碼只會拖慢未來的開發進度,唯一加快進度的方法:始終盡可能保持代碼優雅整潔。
好比醫生在做手術之前要先消毒,你說消毒太耗時間了,直接拿刀子整吧。
作為專業的醫生你會照做么?
作為專業的程序員,我們要了解代碼變壞的風險并堅持保持代碼質量。
什么是整潔代碼
代碼質量評判需要綜合各種因素得到的,我們并不能從單一的維度去評判。
比如代碼可讀性好,但是空間與時間復雜度高,這并不能算得上是好代碼。
好的代碼應該具備:易拓展和維護、簡潔(只做好一件事)、可復用性強(沒有重復代碼)、能快速寫出單元測試。可讀性強、沒有副作用(做了名稱以外的工作)。
易拓展和維護
在不破壞原來的代碼設計下,可以簡單快速的修改和添加代碼實現功能拓展。
簡單地說就是預留了拓展點,將新代碼放在設計的可拓展點,不會因為新增一個功能而改動大量原始代碼。
對修改關閉,對拓展開放,開閉原則。
對于開發而言,我們維護舊代碼的時間超過新項目新代碼的時間。
代碼的可維護性就變得很重要,也就是說代碼分層清晰、模塊劃分精當,滿足高內聚低耦合、抽象出合理的接口,面向接口編程就意味著有較好的可維護性。
同樣的代碼,熟悉他的資深工程師會覺得很容易維護,而新人因為不熟悉代碼,不懂設計模式而無法理解。
所以,易拓展具有主觀性,我們需要提高基礎技能才有資格說代碼是否易拓展和維護。
只做好一件事
單一職責:每個函數、每個類、每個模塊只專注于一件事。
不要設計大而全的類或者函數,我們需要將他們拆分成更細粒度功能更加單一的類。
它不會隱藏設計者的意圖,干凈利落的抽象和直截了當的控制語句。
我們應該讓每個函數每行代碼簡單、邏輯清晰。這樣的話,類依賴和被依賴的類也會變少,減少耦合度。
需要注意的是,也不能拆分太細,否則就會破壞內聚性。
高手,就是用最簡單的方法去解決復雜問題。
沒有重復代碼
在開發過程中,我們應該盡可能抽象出「變與不變」,復用已經存在的代碼,不要寫重復的代碼。
比如運用「封裝、繼承、抽象、多態」特性,代碼封裝成模塊,隱藏變化的細節,暴露不變的接口。
把業務與非業務的代碼邏輯分析,抽象成通用的框架、工具類等。
比如應用模板方法設計模式將不變的算法邏輯框架定義出來,把變化的點延遲到子類重寫。
能快速寫成單元測試
代碼的可測試性差,比較難寫單元測試,那基本上就能說明代碼設計得有問題。
試想下,如果一個類大而全,有一個方法依賴了十幾個外部對象才能完成工作,耦合嚴重。
當你在編寫單元測試的時候,需要 mock 十幾個依賴對象和數據。
那說明這個代碼糟透了,需要合理拆分和設計。
可讀性強
軟件設計大師 Martin Fowler 說過:「Any fool can write code that a computer can understand. Good programmers write code that humans can understand.」
翻譯成中文就是:"任何二貨都會編寫計算機能跑的代碼。優秀的程序員能夠編寫人能夠理解的代碼。”
而可讀性就會涉及到編碼規范、命名、注釋、函數職責是否單一、長度是否精簡。
有數據顯示讀代碼的時間與寫代碼的時間比例超過 10:1,并且編寫當前代碼的難度,取決于讀周邊代碼的難度。
所以我認為可讀性強是最重要的一點。
高質量命名套路
開發過程后命名隨處可見,我們給變量、方法、參數、類、包命名。
而命名的好壞會影響我們的可讀性,我們不妨從命名作為切入口來寫好代碼。
勿模糊,準確達意
在開發過程中,一旦發現更好的名稱,就換掉舊的。
一個變量、方法、或者類的名稱應該展示出它該有的功能。根據名字我們能知道它能做什么事情,如何使用。
如果一個名稱需要大量注釋來補充避免使用者跳坑,那就是糟糕的名字。
- 變量名體現出該字段作用,比如 LocalDate now = LocaDate.now(); now 標識當前時間。
- 防止出現讓人模糊無法理解,必須還要依據大量上下文才能理解的代碼。
- 不要使用魔術。
反例 1 :使用魔數
- // 從數據庫獲取列表
-
List
buyerList = dao.getList(); - buyerList.forEach(x -> {
- for (int i = 1; i <= 5; i++) {
- processedBuyerList.add(String.format("%s,%s", i, x));
- }
- });
你會疑問,為啥索引是從 1 開始?為啥 <= 5。除此之外, i 與 1 極其相似,難以區分。
正確的方式應該使用實際含義的名字讓人理解這么寫的目的,否則維護的人將痛苦不堪。
反例 2:使用生僻字,又臭又長
UltimateAssociatedSubjectRunBatchServiceImpl,當我們看到這樣的類名,是不是不知道怎么讀,也不知道如何搜索和定位,更不知道到底表達的意思是什么,可能命這個名字的人還以為準確表達,其實是“王大媽的裹腳布,又臭又長”。
原本的業務含義是:執行關聯主體任務相關業務類。
鑒于此,我們第一步要避免使用生僻字,可以命名為LinkSubjectServiceImpl ,清晰簡單的表達出關聯主體的業務邏輯都在該類。
不要誤導
盡量不要使用不同之處較小的名稱,這樣讓他人無法一眼區分兩個名稱是啥意思。
例如:函數 deleteIndex 和函數deleteIndexEx,這兩個函數名區別很小了,加之函數 deleteIndexEx后面Ex還是縮寫,也不知道是什么意思,所以他人只能去看函數內容才能明白兩者的區別。
- XYZStringHandler與 XYZStringStorage。
- UserController與 UserInfoController。
讓人抓狂,他們到底是一個東西還是不同的?差別在哪?沒有兩年腦血栓寫不出這樣的。
反例 3:名不副實
下面是一個生成文件并提供下載功能的接口。
- public void downloadExcel(HttpServletResponse response) {
-
List
files = listFile(); - String fileName = System.currentTimeMillis() + ".zip";
- DownloadZip.downLoadFiles(files, filePath);
- DownloadZip.fileDownload(response, filePath, fileName);
- }
我們會疑惑,downLoadFiles 與 fileDownload 到底有啥區別?為啥要調用兩次。
這種真的是十年腦血栓才寫得出來。
downLoadFiles 的功能是創建將 files 打包成 zip 文件,而 fileDownload則是把指定的文件輸出給瀏覽器下載。
所以 downLoadFiles 應該命名為 createZipFile用于合理區分避免誤人子弟。
做有意義的區分
- getActiveOrder();
- getActiveOrderInfo();
- getActiveOrderData();
- getActiveOrders();
上面都是廢話命名,別人你怎么知道到底該調用那個方法?
哪個表示訂單明細?還是歷史訂單,還是全部訂單查詢,廢話是另一種沒有意義的區分。
名稱不同,意思卻無差別。
Order、OrderInfo、OrderData,他們名稱相同 ,意思卻無差別,屬于毫無意義的廢話。如果缺少明確約定,變量moneyAmount就與money沒區別。
Variable一詞永遠不應當出現在變量名中。Table一詞永遠不應當出現在表名中。
結合上下文簡化名稱
- public class Order {
- private String orderNum;
- private String orderCreateTime;
- //...
- }
比如 Order類,在該上下文中,沒必要給每個成員變量重復添加 order 這個前綴單詞,直接命名為 createTime、num。
因為我們可以借助 Order 這個上下文來獲取信息。
- Order order = new Order();
- order.getCreateTime();
名稱易讀、可搜索
可讀指的是不要使用一些生僻字,難以發音的單詞。
可搜索是便于利用 IED 的自動補全和搜索功能,能根據我們的命名規范快速定位想要找的類或者方法等。
可讀
名稱讀不出來,在討論的時候就好像是一個沙雕。
哎,那個「treeNewBeeAxibaKula」類是什么作用?
聽到這樣的名字尷尬癌都犯了。
使用一些生僻字,猶如「王大媽的裹腳布,又長又臭」,沒有兩年腦血栓寫不出這樣的垃圾代碼。
可搜索
IED 很智能,當我們輸入 「Hash」的時候,會列舉出所有 Hash 相關的類。
命名的時候最好符合項目命名習慣,列表數據查詢大家使用 listXXX,你就不要用 queryXXX,統一命名規范,很重要。
包命名
包名統一使用小寫,點分隔符之間有且僅有一個自然語義的英文單詞或者多個單詞自然連接到一塊(如 springframework,deepspace 不需要使用任何分割)。
包名的構成可以分為以下幾四部分【前綴】 【發起者名】【項目名】【模塊名】。
以下表格授權于「Java 填坑筆記」
常見的前綴可以分為以下幾種:
類名
類名使用大駝峰命名形式,應該使用名詞或者名詞短語,比如:Customer、Account。
避免使用 Manager、Processor 等動詞。
接口名除了用名詞和名詞短語以外,還可以使用形容詞或形容詞短語,如 Cloneable,Callable 等,表示實現該接口的類有某種功能或能力。
方法名
方法命名一般為動詞或動詞短語,與參數或參數名共同組成動賓短語,即動詞 + 名詞。一個好的函數名一般能通過名字直接獲知該函數實現什么樣的功能。
布爾返回值的方法
注:Prefix-前綴,Suffix-后綴,Alone-單獨使用
按需執行的方法
用來檢查的方法
異步相關方法
回調方法
操作對象生命周期的方法
4.7 與集合操作相關的方法
與數據相關的方法
成對出現的動詞
單詞 | 意義 |
get獲取 | set 設置 |
add 增加 | remove 刪除 |
create 創建 | destory 移除 |
start 啟動 | stop 停止 |
open 打開 | close 關閉 |
read 讀取 | write 寫入 |
load 載入 | save 保存 |
create 創建 | destroy 銷毀 |
begin 開始 | end 結束 |
backup 備份 | restore 恢復 |
import 導入 | export 導出 |
split 分割 | merge 合并 |
inject 注入 | extract 提取 |
attach 附著 | detach 脫離 |
bind 綁定 | separate 分離 |
view 查看 | browse 瀏覽 |
edit 編輯 | modify 修改 |
select 選取 | mark 標記 |
copy 復制 | paste 粘貼 |
undo 撤銷 | redo 重做 |
insert 插入 | delete 移除 |
add 加入 | append 添加 |
clean 清理 | clear 清除 |
index 索引 | sort 排序 |
find 查找 | search 搜索 |
increase 增加 | decrease 減少 |
play 播放 | pause 暫停 |
launch 啟動 | run 運行 |
compile 編譯 | execute 執行 |
debug 調試 | trace 跟蹤 |
observe 觀察 | listen 監聽 |
build 構建 | publish 發布 |
input 輸入 | output 輸出 |
encode 編碼 | decode 解碼 |
encrypt 加密 | decrypt 解密 |
compress 壓縮 | decompress 解壓縮 |
pack 打包 | unpack 解包 |
parse 解析 | emit 生成 |
connect 連接 | disconnect 斷開 |
send 發送 | receive 接收 |
download 下載 | upload 上傳 |
refresh 刷新 | synchronize 同步 |
update 更新 | revert 復原 |
lock 鎖定 | unlock 解鎖 |
check out 簽出 | check in 簽入 |
submit 提交 | commit 交付 |
push 推 | pull 拉 |
expand 展開 | collapse 折疊 |
begin 起始 | end 結束 |
start 開始 | finish 完成 |
enter 進入 | exit 退出 |
abort 放棄 | quit 離開 |
obsolete 廢棄 | depreciate 廢舊 |
collect 收集 | aggregate 聚集 |
總結
命名目的都是為了讓代碼和工程師進行對話,增強代碼的可讀性,可維護性。優秀的代碼往往能夠見名知意。
本文轉載自微信公眾號「碼哥字節」
原文鏈接:https://mp.weixin.qq.com/s/TEuRxrBFU9CfjmO3vGqtxA