簡潔清爽的代碼風格應該是大多數工程師所期待的。在工作中筆者常常因為起名字而糾結,誇張點可以說是編程5分鐘,命名兩小時!究竟為什麼命名成為了工作中的攔路虎。 ...
簡潔清爽的代碼風格應該是大多數工程師所期待的。在工作中筆者常常因為起名字而糾結,誇張點可以說是編程5分鐘,命名兩小時!究竟為什麼命名成為了工作中的攔路虎。
每個公司都有不同的標準,目的是為了保持統一,減少溝通成本,提升團隊研發效能。所以本文中是筆者結合阿裡巴巴開發規範,以及工作中的見聞針對Java領域相關命名進行整理和總結,僅供參考。
一,Java中的命名規範
好的命名能體現出代碼的特征,含義或者是用途,讓閱讀者可以根據名稱的含義快速釐清程式的脈絡。不同語言中採用的命名形式大相徑庭,Java中常用到的命名形式共有三種,既首字母大寫的UpperCamelCase,首字母小寫的lowerCamelCase以及全部大寫的並用下劃線分割單詞的UPPER_CAMEL_UNSER_SCORE。通常約定,類一般採用大駝峰命名,方法和局部變數使用小駝峰命名,而大寫下劃線命名通常是常量和枚舉中使用。
類型 | 約束 | 例 |
---|---|---|
項目名 | 全部小寫,多個單詞用中劃線分隔‘-’ | spring-cloud |
包名 | 全部小寫 | com.alibaba.fastjson |
類名 | 單詞首字母大寫 | Feature, ParserConfig,DefaultFieldDeserializer |
變數名 | 首字母小寫,多個單片語成時,除首個單詞,其他單詞首字母都要大寫 | password, userName |
常量名 | 全部大寫,多個單詞,用'_'分隔 | CACHE_EXPIRED_TIME |
方法 | 同變數 | read(), readObject(), getById() |
二,包命名
包名統一使用小寫,點分隔符之間有且僅有一個自然語義的英文單詞或者多個單詞自然連接到一塊(如 springframework,deepspace不需要使用任何分割)。包名統一使用單數形式,如果類命有複數含義,則可以使用複數形式。
包名的構成可以分為以下幾四部分【首碼】 【發起者名】【項目名】【模塊名】。常見的首碼可以分為以下幾種:
首碼名 | 例 | 含義 |
---|---|---|
indi(或onem ) | indi.發起者名.項目名.模塊名.…… | 個體項目,指個人發起,但非自己獨自完成的項目,可公開或私有項目,copyright主要屬於發起者。 |
pers | pers.個人名.項目名.模塊名.…… | 個人項目,指個人發起,獨自完成,可分享的項目,copyright主要屬於個人 |
priv | priv.個人名.項目名.模塊名.…… | 私有項目,指個人發起,獨自完成,非公開的私人使用的項目,copyright屬於個人。 |
team | team.團隊名.項目名.模塊名.…… | 團隊項目,指由團隊發起,並由該團隊開發的項目,copyright屬於該團隊所有 |
頂級功能變數名稱 | com.公司名.項目名.模塊名.…… | 公司項目,copyright由項目發起的公司所有 |
三,類命名
類名使用大駝峰命名形式,類命通常時名詞或名詞短語,介面名除了用名詞和名詞短語以外,還可以使用形容詞或形容詞短語,如Cloneable,Callable等,表示實現該介面的類有某種功能或能力。對於測試類則以它要測試的類開頭,以Test結尾,如HashMapTest。
對於一些特殊特有名詞縮寫也可以使用全大寫命名,比如XMLHttpRequest,不過筆者認為縮寫三個字母以內都大寫,超過三個字母則按照要給單詞算。這個沒有標準如阿裡巴巴中fastjson用JSONObject作為類命,而google則使用JsonObjectRequest命名,對於這種特殊的縮寫,原則是統一就好。
屬性 | 約束 | 例 |
---|---|---|
抽象類 | Abstract 或者 Base 開頭 | BaseUserService |
枚舉類 | Enum 作為尾碼 | OSType |
工具類 | Utils作為尾碼 | StringUtils |
異常類 | Exception結尾 | RuntimeException |
介面實現類 | 介面名+ Impl | UserServiceImpl |
領域模型相關 | /DO/DTO/VO/DAO | 正例:UserDAO 反例: UserDo, UserDao |
設計模式相關類 | Builder,Factory等 | 當使用到設計模式時,需要使用對應的設計模式作為尾碼,如ThreadFactory |
處理特定功能的 | Handler,Predicate, Validator | 表示處理器,校驗器,斷言,這些類工廠還有配套的方法名如handle,predicate,validate |
測試類 | Test結尾 | UserServiceTest, 表示用來測試UserService類的 |
MVC分層 | Controller,Service,ServiceImpl,DAO尾碼 | UserManageController,UserManageDAO |
四,方法
方法命名採用小駝峰的形式,首字小寫,往後的每個單詞首字母都要大寫。 和類名不同的是,方法命名一般為動詞或動詞短語,與參數或參數名共同組成動賓短語,即動詞 + 名詞。一個好的函數名一般能通過名字直接獲知該函數實現什麼樣的功能。
4.1 返回真偽值的方法
註:Prefix-首碼,Suffix-尾碼,Alone-單獨使用
位置 | 單詞 | 意義 | 例 |
---|---|---|---|
Prefix | is | 對象是否符合期待的狀態 | isValid |
Prefix | can | 對象能否執行所期待的動作 | canRemove |
Prefix | should | 調用方執行某個命令或方法是好還是不好,應不應該,或者說推薦還是不推薦 | shouldMigrate |
Prefix | has | 對象是否持有所期待的數據和屬性 | hasObservers |
Prefix | needs | 調用方是否需要執行某個命令或方法 | needsMigrate |
4.2 用來檢查的方法
單詞 | 意義 | 例 |
---|---|---|
ensure | 檢查是否為期待的狀態,不是則拋出異常或返回error code | ensureCapacity |
validate | 檢查是否為正確的狀態,不是則拋出異常或返回error code | validateInputs |
4.3 按需求才執行的方法
位置 | 單詞 | 意義 | 例 |
---|---|---|---|
Suffix | IfNeeded | 需要的時候執行,不需要的時候什麼都不做 | drawIfNeeded |
Prefix | might | 同上 | mightCreate |
Prefix | try | 嘗試執行,失敗時拋出異常或是返回errorcode | tryCreate |
Suffix | OrDefault | 嘗試執行,失敗時返回預設值 | getOrDefault |
Suffix | OrElse | 嘗試執行、失敗時返回實際參數中指定的值 | getOrElse |
Prefix | force | 強制嘗試執行。error拋出異常或是返回值 | forceCreate, forceStop |
4.4 非同步相關方法
位置 | 單詞 | 意義 | 例 |
---|---|---|---|
Prefix | blocking | 線程阻塞方法 | blockingGetUser |
Suffix | InBackground | 執行在後臺的線程 | doInBackground |
Suffix | Async | 非同步方法 | sendAsync |
Suffix | Sync | 對應已有非同步方法的同步方法 | sendSync |
Prefix or Alone | schedule | Job和Task放入隊列 | schedule, scheduleJob |
Prefix or Alone | post | 同上 | postJob |
Prefix or Alone | execute | 執行非同步方法(註:我一般拿這個做同步方法名) | execute, executeTask |
Prefix or Alone | start | 同上 | start, startJob |
Prefix or Alone | cancel | 停止非同步方法 | cancel, cancelJob |
Prefix or Alone | stop | 同上 | stop, stopJob |
4.5 回調方法
位置 | 單詞 | 意義 | 例 |
---|---|---|---|
Prefix | on | 事件發生時執行 | onCompleted |
Prefix | before | 事件發生前執行 | beforeUpdate |
Prefix | pre | 同上 | preUpdate |
Prefix | will | 同上 | willUpdate |
Prefix | after | 事件發生後執行 | afterUpdate |
Prefix | post | 同上 | postUpdate |
Prefix | did | 同上 | didUpdate |
Prefix | should | 確認事件是否可以發生時執行 | shouldUpdate |
4.6 操作對象生命周期的方法
單詞 | 意義 | 例 |
---|---|---|
initialize | 初始化。也可作為延遲初始化使用 | initialize |
pause | 暫停 | onPause ,pause |
stop | 停止 | onStop,stop |
abandon | 銷毀的替代 | abandon |
destroy | 同上 | destroy |
dispose | 同上 | dispose |
4.7 與集合操作相關的方法
單詞 | 意義 | 例 |
---|---|---|
contains | 是否持有與指定對象相同的對象 | contains |
add | 添加 | addJob |
append | 添加 | appendJob |
insert | 插入到下標n | insertJob |
put | 添加與key對應的元素 | putJob |
remove | 移除元素 | removeJob |
enqueue | 添加到隊列的最末位 | enqueueJob |
dequeue | 從隊列中頭部取出並移除 | dequeueJob |
push | 添加到棧頭 | pushJob |
pop | 從棧頭取出並移除 | popJob |
peek | 從棧頭取出但不移除 | peekJob |
find | 尋找符合條件的某物 | findById |
4.8 與數據相關的方法
單詞 | 意義 | 例 |
---|---|---|
create | 新創建 | createAccount |
new | 新創建 | newAccount |
from | 從既有的某物新建,或是從其他的數據新建 | fromConfig |
to | 轉換 | toString |
update | 更新既有某物 | updateAccount |
load | 讀取 | loadAccount |
fetch | 遠程讀取 | fetchAccount |
delete | 刪除 | deleteAccount |
remove | 刪除 | removeAccount |
save | 保存 | saveAccount |
store | 保存 | storeAccount |
commit | 保存 | commitChange |
apply | 保存或應用 | applyChange |
clear | 清除數據或是恢復到初始狀態 | clearAll |
reset | 清除數據或是恢復到初始狀態 | resetAll |
4.9 成對出現的動詞
單詞 | 意義 |
---|---|
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 聚集 |
五,變數&常量命名
5.1 變數命名
變數是指在程式運行中可以改變其值的量,包括成員變數和局部變數。變數名由多單片語成時,第一個單詞的首字母小寫,其後單詞的首字母大寫,俗稱駱駝式命名法(也稱駝峰命名法),如 computedValues,index、變數命名時,儘量簡短且能清楚的表達變數的作用,命名體現具體的業務含義即可。
變數名不應以下劃線或美元符號開頭,儘管這在語法上是允許的。變數名應簡短且富於描述。變數名的選用應該易於記憶,即,能夠指出其用途。儘量避免單個字元的變數名,除非是一次性的臨時變數。pojo中的布爾變數,都不要加is(資料庫中的布爾欄位全都要加 is_ 首碼)。
5.2 常量命名
常量命名CONSTANT_CASE,一般採用全部大寫(作為方法參數時除外),單詞間用下劃線分割。那麼什麼是常量呢?
常量是在作用域內保持不變的值,一般使用final進行修飾。一般分為三種,全局常量(public static final修飾),類內常量(private static final 修飾)以及局部常量(方法內,或者參數中的常量),局部常量比較特殊,通常採用小駝峰命名即可。
/**
* 一個demo
*
* @author Jann Lee
* @date 2019-12-07 00:25
**/
public class HelloWorld {
/**
* 局部常量(正例)
*/
public static final long USER_MESSAGE_CACHE_EXPIRE_TIME = 3600;
/**
* 局部常量(反例,命名不清晰)
*/
public static final long MESSAGE_CACHE_TIME = 3600;
/**
* 全局常量
*/
private static final String ERROR_MESSAGE = " error message";
/**
* 成員變數
*/
private int currentUserId;
/**
* 控制台列印 {@code message} 信息
*
* @param message 消息體,局部常量
*/
public void sayHello(final String message){
System.out.println("Hello world!");
}
}
常量一般都有自己的業務含義,不要害怕長度過長而進行省略或者縮寫。如,用戶消息緩存過期時間的表示,那種方式更佳清晰,交給你來評判。
通用命名規則
- 儘量不要使用拼音;杜絕拼音和英文混用。對於一些通用的表示或者難以用英文描述的可以採用拼音,一旦採用拼音就堅決不能和英文混用。
正例: BeiJing, HangZhou
反例: validateCanShu - 命名過程中儘量不要出現特殊的字元,常量除外。
- 儘量不要和jdk或者框架中已存在的類重名,也不能使用java中的關鍵字命名。
- 妙用介詞,如for(可以用同音的4代替), to(可用同音的2代替), from, with,of等。
如類名採用UserService4MySqlDAO,方法名getUserInfoFromRedis,convertJson2Map等。
六,代碼註解
6.1 註解的原則
好的命名增加代碼閱讀性,代碼的命名往往有嚴格的限制。而註解不同,程式員往往可以自由發揮,單並不意味著可以為所欲為之胡作非為。優雅的註解通常要滿足三要素。
- Nothing is strange
沒有註解的代碼對於閱讀者非常不友好,哪怕代碼寫的在清除,閱讀者至少從心理上會有抵觸,更何況代碼中往往有許多複雜的邏輯,所以一定要寫註解,不僅要記錄代碼的邏輯,還有說清楚修改的邏輯。 - Less is more
從代碼維護角度來講,代碼中的註解一定是精華中的精華。合理清晰的命名能讓代碼易於理解,對於邏輯簡單且命名規範,能夠清楚表達代碼功能的代碼不需要註解。濫用註解會增加額外的負擔,更何況大部分都是廢話。
// 根據id獲取信息【廢話註解】
getMessageById(id)
- Advance with the time
註解應該隨著代碼的變動而改變,註解表達的信息要與代碼中完全一致。通常情況下修改代碼後一定要修改註解。
6.2 註解格式
註解大體上可以分為兩種,一種是javadoc註解,另一種是簡單註解。javadoc註解可以生成JavaAPI為外部用戶提供有效的支持javadoc註解通常在使用IDEA,或者Eclipse等開發工具時都可以自動生成,也支持自定義的註解模板,僅需要對對應的欄位進行解釋。參與同一項目開發的同學,儘量設置成相同的註解模板。
6.2.1 類註接
javadoc註解中,每個類都必須有註解。
/**
* Copyright (C), 2019-2020, Jann balabala...
*
* 類的介紹:這是一個用來做什麼事情的類,有哪些功能,用到的技術.....
*
* @author 類創建者姓名 保持對齊
* @date 創建日期 保持對齊
* @version 版本號 保持對齊
*/
6.2.2 屬性註解
在每個屬性前面必須加上屬性註釋,通常有一下兩種形式,至於怎麼選擇,你高興就好,不過一個項目中要保持統一。
/** 提示信息 */
private String userName;
/**
* 密碼
*/
private String password;
6.2.3 方法註釋
在每個方法前面必須加上方法註釋,對於方法中的每個參數,以及返回值都要有說明。
/**
* 方法的詳細說明,能幹嘛,怎麼實現的,註意事項...
*
* @param xxx 參數1的使用說明, 能否為null
* @return 返回結果的說明, 不同情況下會返回怎樣的結果
* @throws 異常類型 註明從此類方法中拋出異常的說明
*/
6.2.4 構造方法註釋
在每個構造方法前面必須加上註釋,註釋模板如下:
/**
* 構造方法的詳細說明
*
* @param xxx 參數1的使用說明, 能否為null
* @throws 異常類型 註明從此類方法中拋出異常的說明
*/
而簡單註解往往是需要工程師位元組定義,在使用註解時應該註意一下幾點:
枚舉類的各個屬性值都要使用註解,枚舉可以理解為是常量,通常不會發生改變,通常會被在多個地方引用,對枚舉的修改和添加屬性通常會帶來很大的影響。
保持排版整潔,不要使用行章節附註釋;雙斜杠和星號之後要用1個空格分隔。
int id = 1;// 反例:不要使用行章節附註釋 //反例:換行符與註釋之間沒有縮進 int age = 18; // 正例:姓名 String name; /** * 多行註釋123 * 2333333 */
參考文獻:
《碼出高效》
https://www.cnblogs.com/wangcp-2014/p/10215620.html
https://qiita.com/KeithYokoma/items/2193cf79ba76563e3db6
https://google.github.io/styleguide/javaguide.html#s2.1-file-name
如果你想要在技術上有所成就,想要結識志同道合的朋友,可以關註筆者的wx公眾號 cruder,大家一起進步