有意义的命名:名副其实、避免误导、做有意义的区分、使用读得出来的名称

好,咱们今天聊聊命名。说实话,代码里最常被忽视但又最要命的问题,就是命名。

我见过太多项目,代码逻辑本身没问题,但读起来像在猜谜。变量名叫 abtemp,函数名叫 doStuffhandleIt。你想想看,这种代码三个月后你自己回来看,还能记得 temp 到底是临时存什么的吗?

命名这件事,说白了就是给代码里的每个元素起个合适的名字。名字起好了,代码自己就能说话。起不好,就得靠注释来擦屁股。

核心原则:好的命名应该让读者只看名字就知道它是什么、干什么、怎么用。

名副其实:名字要说出真相

什么叫名副其实?就是变量名、函数名、类名要准确反映它的含义和用途。别玩文字游戏,别搞抽象艺术。

举个例子,你看到下面这段代码,能猜出 d 是什么吗?

int d; // 经过的天数

嗯,注释写了是“经过的天数”。但问题是,为什么不用 elapsedDays 呢?

我个人习惯是:如果一个变量需要注释来解释,那说明名字没起好。好的名字应该自带文档属性。

int elapsedDays; // 这才对,一目了然

再看一个函数命名的例子:

// 糟糕的命名
public List<int[]> getThem() {
    List<int[]> list1 = new ArrayList<>();
    for (int[] x : theList) {
        if (x[0] == 4) {
            list1.add(x);
        }
    }
    return list1;
}

这段代码里,theList 是什么?x[0] 是什么?4 又代表什么?完全看不懂。

重构一下:

public List<int[]> getFlaggedCells() {
    List<int[]> flaggedCells = new ArrayList<>();
    for (int[] cell : gameBoard) {
        if (cell[STATUS_VALUE] == FLAGGED) {
            flaggedCells.add(cell);
        }
    }
    return flaggedCells;
}

现在清楚多了吧?gameBoard 是棋盘,cell 是格子,FLAGGED 是标记状态。名字自己就把故事讲完了。

小技巧:起名字时,试着问自己一个问题——“如果别人只看这个名字,能猜出它大概的用途吗?” 如果答案是否定的,那就换一个。

避免误导:别让名字撒谎

这一点我特别有感触。我在项目中遇到过好几次,因为命名误导导致的线上bug。

最常见的误导就是缩写和简写。比如 hp,你以为是“惠普”?其实是 hypotenuse(斜边)。再比如 cust,到底是 customer 还是 custom

还有更隐蔽的误导:

  • list 命名一个不是 List 类型的变量
  • accountGroup 命名一个普通的字符串
  • isActive 命名一个返回 int 的方法(0/1 表示真假)

这些都会让读者产生错误的预期。代码读起来就像在玩“猜猜我到底是什么”的游戏。

避坑指南:我曾经把一个返回布尔值的方法命名为 checkStatus(),结果同事以为它只是检查状态而不返回结果,直接忽略了返回值。后来我改成 isStatusValid(),问题就解决了。

另外,别用发音相似或者形状相似的字母组合。比如 l1O0。你想想看,l110 在代码里能分得清吗?

做有意义的区分:别为了区分而区分

这个坑我踩过很多次。有时候为了满足编译器的要求(比如不能有两个同名变量),就随便加个数字或者前缀来区分。

比如:

public void copyChars(char a1[], char a2[]) {
    for (int i = 0; i < a1.length; i++) {
        a2[i] = a1[i];
    }
}

a1a2 有什么区别?哪个是源,哪个是目标?完全看不出来。

改成这样:

public void copyChars(char source[], char destination[]) {
    for (int i = 0; i < source.length; i++) {
        destination[i] = source[i];
    }
}

现在清楚了:source 是源,destination 是目标。

还有一种常见的无意义区分:加“Info”、“Data”、“Object”这类后缀。

// 糟糕
Product productData;
Product productInfo;
Product productObject;

// 好
Product product;

你想想看,productDataproductInfo 到底有什么区别?如果没区别,那为什么要加后缀?

原则:如果两个名字不能让人一眼看出区别,那它们就是无意义的区分。删掉多余的词,或者重新起名。

使用读得出来的名称:别让嘴巴打结

这一点可能很多人没意识到。代码不仅要写给机器看,更要写给人看。人是用嘴巴和耳朵来思考的。

我见过一个变量名叫 genymdhms(生成日期:年、月、日、时、分、秒)。你试试读出来:“gen-y-m-d-h-m-s”?这哪是变量名,这分明是密码。

改成 generationTimestamp 或者 createDate,是不是顺口多了?

再比如:

// 糟糕
private Date crDt;

// 好
private Date createDate;

crDt 读出来是“C-R-D-T”,四个字母。而 createDate 读出来是“create date”,两个单词。哪个更容易记住?哪个更容易在讨论时说出来?

我的习惯:每次起完名字,我会自己读一遍。如果读起来拗口,或者需要拼字母,那就说明名字没起好。好的名字应该像日常对话一样自然。

这一点在团队协作中尤其重要。你想想看,开会讨论代码时,你说“把那个 crDt 改一下”,别人得反应半天。但你说“把 createDate 改一下”,大家立刻就知道你在说什么。

知识体系总览

下面这张图总结了有意义的命名的核心要点:

有意义的命名 · 知识体系 有意义的命名 名副其实 避免误导 有意义的区分 读得出来 变量名反映含义 函数名说明行为 类名描述职责 无需注释解释 不用模糊缩写 类型名要准确 避免形似字母 不产生错误预期 不用数字后缀 不用Info/Data 每个名字有独特含义 删掉冗余词 用完整单词 避免缩写拼凑 读起来像对话 便于团队讨论 核心目标:代码自解释,无需额外文档 好名字 = 好代码 = 好维护

总结一下

命名这件事,说难不难,说简单也不简单。它考验的不是你的技术能力,而是你对代码的敬畏心。

我这些年写代码最大的体会就是:好的命名,是对未来读代码的人的一种尊重。那个未来读代码的人,很可能就是三个月后的你自己。

所以,下次起名字的时候,多花30秒想一想:这个名字够不够清楚?会不会误导人?有没有更好的选择?

嗯,这30秒的投资,回报率绝对高得吓人。

一句话记住:名副其实、避免误导、做有意义的区分、使用读得出来的名称。这四条原则,够你用一辈子。

公众号:蓝海资料掘金营,微信deep3321