工程實踐:給函式取一個"好"的名字

Matrix海子發表於2019-03-22

  早在2013年,國外有個程式設計師做了一個有意思的投票統計(原始連結請見:《程式設計師:你認為最難做的事情是什麼?》),該投票是讓程式設計師從以下幾個選項中選出平時在工作中自己認為最難做的事情:

  • 做專案方案設計
  • 編寫測試用例
  • 撰寫設計文件
  • 向別人解釋我們在做什麼事情
  • 實現你不認同的feature
  • 在別人寫的程式碼基礎上做改造
  • 與人溝通
  • 給函式、變數命名
  • 進行工作量估時

  也許在大家的印象中,撰寫設計文件在別人寫的程式碼基礎上做改造應該是最難的事情。但是最終的投票結果確讓大家意想不到,這次投票一起有4522名程式設計師參與了投票,排在第一位的是給函式、變數命名:

  大概一半的人投票給了給函式、變數命名,從這次投票結果我們足可以看出:給函式、變數命名雖然是一件再普通不過的事情,但是要想把這件事做好絕非易事。那麼今天,我們就來聊一聊如何給函式取一個好的名字。以下是本文大綱目錄:

  一.常見的函式命名風格

  二.函式命名的最高境界

  三.函式命名最佳實踐

  若有不正之處請多多諒解,並歡迎批評指正。

  請尊重作者勞動成果,轉載請標明原文連結:

 https://www.cnblogs.com/dolphin0520/p/10567879.html

  

一.常見函式命名風格

  目前來說,最常見的函式命名主要有兩種風格:駝峰命名和帕斯卡命名。

  • 駝峰命名:多個單片語成一個名稱時,第一個單詞全部小寫,後面單詞首字母大寫;如:
public void setUserName(String userName);複製程式碼
  • 帕斯卡命名:多個單片語成一個名稱時,每個單詞的首字母大寫;
public void SetUserName(String userName);複製程式碼

  兩種命名風格都是ok的,但要保證一點,對於一個團隊或者一個專案,需要根據語言本身的推薦命名方式做好約定。比如java一般都採取駝峰命名,C#採取帕斯卡命名。

二. 函式命名最高境界

  我們通常說:天下武功,唯快不破。那麼對於函式命名來說最高境界是什麼呢?我認為是:見字如面,顧名思義,就是看到函式的名字就知道這個函式具體做了哪些事情。

  比如上面的函式:

public void setUserName(String userName);複製程式碼

  但是下面這個函式命名就不是一個好的命名:

public String addCharacter(String originString, char ch);複製程式碼

  這個函式,一咋看,還不錯,從函式字面意思看是給某個字串新增一個字元。但是到底是在原有字串首部新增,還是在原有字串末尾追加呢?亦或是在某個固定位置插入呢?從函式名字完全看不出來這個函式的真正意圖,只能繼續往下讀這個函式的具體實現才知道。

  而下面這幾個名字就比上面要好得多:

public String appendCharacter(String originString, char ch);     // 追加到末尾
public String insertCharacter(String originString, char ch, int insertPosition); // 插入指定位置複製程式碼

三. 函式命名最佳實踐

1)要領1:動詞選取要精準

  通常來說,動詞決定了一個函式要採取什麼"動作"。動詞取的好,一個函式名字已經成功了80%。

  常用動詞表:

類別

單詞

新增/插入/建立/初始化/載入

add、append、insert、create、initialize、load

刪除/銷燬

delete、remove、destroy、drop

開啟/開始/啟動

open、start

關閉/停止

close、stop

獲取/讀取/查詢/查詢

get、fetch、acquire、read、search、find、query

設定/重置/放入/寫入/釋放/重新整理

set、reset、put、write、release、refresh

傳送/推送

send、push

接收/拉取

receive、pull

提交/撤銷/取消

submit、cancel

收集/採集/選取/選擇

collect、pick、select

提取/解析

sub、extract、parse

編碼/解碼

encode、decode

填充/打包/壓縮

fill、pack、compress

清空/拆包/解壓

flush、clear、unpack、decompress

增加/減少

increase、decrease、reduce

分隔/拼接

split、join、concat

過濾/校驗/檢測

filter、valid、check

2)要領2:名詞使用領域詞彙

  動詞決定了函式的具體動作,而名詞決定了函式具體的操作物件,對於名詞,儘量使用領域詞彙,不要使用生僻或者大家很少使用的詞語。

  舉個例子:集合的容量通常用capacity、集合實際元素個數用size、字串長度用length,這種就遵循大家的使用習慣,不要用size去形如字串的長度。

  再比如,假如使用到建造者模式,那麼通常會用build作為函式名字,這個時候就不要另闢蹊徑,用create來作為函式名字,使用大家約定俗成的命名習慣更容易讓你的程式碼被別人讀懂。

  常用名詞表:

類別

單詞

容量/大小/長度

capacity、size、length

例項/上下文

instance、context

配置

config、settings

頭部/前面/前一個/第一個

header、front、previous、first

尾部/後面/後一個/最後一個

tail、back、next、last

區間/區域/某一部分/範圍/規模

range、interval、region、area、section、scope、scale

快取/緩衝/會話

cache、buffer、session

本地/區域性/全域性

local、global

成員/元素

member、element

選單/列表

menu、list

源/目標

source、destination、target

3)要領3:函式取名最忌諱"名不副實"

  函式取名最忌諱的是"名不副實",舉個例子,假如有個Cache類,裡面有個函式判斷key是否過期:

public boolean isExpired(String key) {
        // 當前時間戳
        long curTimestamp = DateUtils.nowUnixTime();
        // 獲取key的存入時間戳
        long storeTimestamp = getStoreTimestamp(key);
       
        if (curTimestamp - storeTimestamp > MAX_EXPIRE_SECONDS) {
            // 注意這個地方的delete是個隱藏邏輯
            delete(key);
            return true;
        }
        return false;
 }
複製程式碼

  上面這個函式從函式字面意思看是判斷key是否過期,但是!!它居然在函式裡面隱藏了一段特殊邏輯:如果過期則刪除掉key。這個就是典型的"名不副實",這個是最忌諱的,會給後續的開發人員留下"巨坑"。

  有兩種方式去優化這段程式碼:

  • 方式一:將隱藏邏輯去掉
public boolean isExpired(String key) {
        // 當前時間戳
        long curTimestamp = DateUtils.nowUnixTime();
        // 獲取key的存入時間戳
        long storeTimestamp = getStoreTimestamp(key);
       
        if (curTimestamp - storeTimestamp > MAX_EXPIRE_SECONDS) {
            return true;
        }
        return false;
 }複製程式碼
  • 方式二:改變函式名字
public int deleteIfExpired(String key) {
        // 當前時間戳
        long curTimestamp = DateUtils.nowUnixTime();
        // 獲取key的存入時間戳
        long storeTimestamp = getStoreTimestamp(key);
       
        if (curTimestamp - storeTimestamp > MAX_EXPIRE_SECONDS) {
            return delete(key);
        }
        return 0;
 }複製程式碼

4)要領4:多查詢條件的函式名字謹慎使用介詞by

  我們平時在寫查詢介面時,假如有多個查詢引數怎麼辦?每個通過by一起連線依賴?No!這絕對不是明智的方式。假如一開始產品的需求是通過學生姓名查詢學生資訊,寫出來的可能是這樣的函式:

public List<Student> getByName(String name);
複製程式碼

  然後突然又有一天產品提出了新的需求,希望同時可以通過姓名和電話號碼來查詢學生資訊,那麼函式可能變成這樣了:

public List<Student> getByNameAndMobile(String name, String mobile);
複製程式碼

  接著,沒過多久,產品又希望根據學生年齡來查詢學生資訊,那麼函式可能變成這樣了:

public List<Student> getByNameAndMobileAndAge(String name, String mobile, int age);
複製程式碼

  如果這樣來給函式命名,那麼你的噩夢大門即將開啟。

  通常比較好的做法是:

  • 如果是通過主鍵id來查詢,那麼可以通過by來連線查詢資訊,比如:

public Student getByStudentId(long studentId);複製程式碼
  • 如果是通過其他屬性來查詢,並且未來會存在多個組合查詢的可能性,建議進行封裝,比如:

public List<Student> getStudents(StudentSearchParam searchParam);
複製程式碼

  

  最後,建議大家平時在寫程式碼過程中,不要怕在函式命名上耗費時間,一個好的函式命名在後期會大大減少你程式碼重構的成本,爭取對函式命名做到"見字如面"~


相關文章