《代码整洁之道》读书笔记(一)之取个好的名字

阅读《代码整洁之道》有两种原因,第一种:你是个程序员;第二,你想成为更好的程序员。
读完后,你能获得如下技能:

  • 知道更多关于代码的事儿
  • 辨别好代码和糟糕代码的差异
  • 学会如何写出好代码
  • 学会如何将糟糕代码改成好代码

Later equals never. 有些事儿现在不做以后都不会做了。

程序员基础价值谜题

以前混乱的代码拖了自己后腿,但开发者背负着期限的压力,只好继续制造混乱。而制作混乱无助于赶上期限。

赶上期限的唯一办法:始终保持代码的整洁。

写整洁代码就像是绘画。多数人知道一幅画是好是坏,但能分辨优劣并不表示懂得绘画,能分辨代码优劣的人也不意味着会写整洁代码。

写整洁代码需要遵循大量的小技巧,贯彻刻苦习得的“整洁感”。这种“代码感”就是关键所在。

什么是整洁代码

整洁的代码只做好一件事。简洁,优雅。

每个函数、每个类、每个模块都全神贯注于一事,不受四周细节的干扰和污染。

有意义的命名

变量、函数、参数、类、包、文件。有很多地方需要命名。怎么命名才能简洁明了?

1. 名副其实

说起来简单,但这是一个很严肃的问题。选个好名字要花时间,但省下来的时间比花掉的多。一旦有好的命名,就换掉旧的。

  • 如果名称需要注释来补充,那么说明名字没取好。(不是名副其实)
// 差的命名
int d;          // 消逝的时间,以日计算。
    
// 好的命名
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;
  • 选择体现本意的名称能让人更容易理解和修改代码。
public List<int[]> getThem() {
    List<int[]> list1 = new ArrayList<int[]>();
    for (int[] x : theList) {
        if (x[0] == 4) {
            list1.add(x);
        }
    }
    return list1;
}

上面代码虽然简洁,但是我们能说清楚它到底做了啥吗? 问题不在于代码的简洁度,而是在于代码的模糊度:即上下文在代码中未被明确体现的程度。上面代码要求我们了解类似一下问题的答案:

  1. theList中是什么类型的东西
  2. theList零下标条目的意义是什么
  3. 值4的意义是什么
  4. 我怎么使用返回的列表

问题的答案没体现在代码段中,而这本就是它们应该在的地方。比如,我们正在开发一款扫雷游戏,我们发现盘面是名为theList的单元格列表,那就将其名称改为gameBoard。

盘面上每个单元格都用一个简单数组表示。我们还发现,零下标条目是一种状态的值,而这种状态值为4表示为“已标记”。只要改为有意义的名称,代码就会得到相当程度的改进。

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

还可以更进一步,不用int数组表示单元格,而是另写一个类。该类包括一个名副其实的函数(称为isFlagged),从而掩盖住那个魔术数(就是那个4)。于是得到函数的新版本:

public List<Cell> getFlaggedCells() {
    List<Cell> flaggedCells = new ArrayList<>();
    for (Cell cell : gameBoard) {
        if (cell.isFlagged()) {
            flaggedCells.add(cell);
        }
    }
    return flaggedCells;
}

只要简单改一下名字,就能轻易知道发生了什么。这就是选好名字的力量

2. 避免误导

程序员必须避免留下掩盖代码本意的错误线索。应当避免使用与本意相悖的词。例如,别用accountList来指称一组账号,除非它真的是List类型(即便容器就是一个List,最好也别在名称中写出容器类型名)。如果包含账号的容器并非真是个List,就会引起错误的判断。所以,用accountGroup或bunchOfAccounts,甚至直接用accounts都会好一些。

提防使用不同之处较小的名称。相区分模块中某处的XYZControllerForEfficientHandlongOfStrings和另一处的XYZControllerForEfficientStorageOfStrings,会花多长时间呢? 这两个词外形实在是太像了。

误导性名称真正可怕的例子是使用小写的字母l和大写的字母O作为变量名,尤其是在组合使用的时候。问题在于,它俩看起来完全像是数字1和0。

3. 做有意义的区分

数字系列和废话名称,可以满足编译器,但是远远不够。

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

以数字系列命名(a1, a2, ......aN)是依义命名的对立面。这样的名称纯属误导——完全没有提供正确的信息,没有提供导向作者意图的线索。

如果将参数名改为source和destination,这个函数将会像样很多。

废话是另一种没有意义的区分。假设你有一个Product类。如果还有一个ProductInfo或ProductData类,那它们的名称虽然不同,意思却没区别。Info和Data就像a、an和the一样,是意义含混的废话。

如果缺少明确约定,变量moneyAmout就与money没区别,customerInfo与customer没区别,accountData与account没区别,theMessage也与message没区别。要区分名称,就要以读者能鉴别不同之处的方式来区分。

4. 使用读的出来的名称

能读出来的名称更容易记忆。如果名称读不出来,讨论的时候就像个傻鸟。

Date genymdhms // 生成日期,年、月、日、时、分、秒

Date generationTimestamp;

5. 使用可搜索的名称

MAX_CLASSES_PER_STUDENT比找数字7要容易得多。同样字母e也不是个便于搜索的变量名。因为太常见了。

单字母名称仅用于短方法中的局部变量。名称长短应与其作用域大小相对应。如果变量或常量可能在代码中多处使用,则应赋予其以便于搜索的名字。

6. 避免将类型或作用域编进名称中

  • 匈牙利语标记法(Hungarian Notation,HN)

早期编译器不做类型检查,程序员需要用匈牙利语标记法(Hungarian Notation,HN)来帮助自己记住类型。

在Windows的C语言API的时代,HN非常重要,传说HN是为了纪念具有传奇色彩的微软程序员Charles Simonyi。这种标记法比较简单:即变量名以表明该变量数据类型的小写字母开始。

例如szCmdLine的前缀sz代表string end of zero.以0 结尾的字符串。

strPhone,代表Phone是字符串类型。

而今,大部分语言是强类型的,代码编辑环境都已经先进到在编译开始前就侦测到类型错误的程度!所以HN和其他类似的格式编码都多余了。

它们增加了修改变量,函数,或类的名称或类型的难度

PhoneNumber phoneString;

//类型变化时,名称并未变化。
  • 成员前缀

也不必用 m_ 前缀来标明成员变量。应当把类和函数做得足够小,消除对成员前缀的需要。

  • 接口和实现

接口前导字母I被滥用。

7. 类名应该是名词或名词短语,方法名应该是动词或者动词短语

类名如Customer、WikiPage、Account。避免使用Manager、Processor、Data或Info这样的类名

方法名如postPayment、deletePage或save。属性访问器、修改器或断言应该根据其值命名,并依Javabean标准加上get、set和is前缀。

8. 同一概念对应统一个词

比如get、fetch、retrieve表达的含义差不多,尽量保持多个类使用同一种写法。

Controller、Manager、Driver含义相近,尽量保持一致。

9. 不用双关语

10. 使用解决方案领域名称

代码是给程序员读的,用计算机领域类的术语来命名是一个很好的做法。比如AccountVisitor、JobQueue这种。

如果不能用程序员熟悉的术语来命名,就采用所涉问题的领域名称命名。

11. 添加有意义的语境、不要添加没用的语境

如果你有命名良好的类、函数或命名空间来放置名称,给读者提供语境是最好不过了。如果没有,就用最后一招———给名称添加前缀。

比如,你有名为firstName、lastName、street、hourseNumber、city、state变量,搁一块儿的时候,很明确是一个地址。如果只是在一个方法中单独的看到一个state变量呢,你会理所当然得推断是一个地址吗?

可以添加前缀addrFirstName、addrLastName、addrState来提供语境。

假设有一个名为“加油站豪华版”(Gas Station Deluxe)的应用,在其中给每个类添加GSD前缀就不是什么好点子了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容