个人精进系列-好的代码-命名/注释

为什么需要好的代码?

大部分时间用于阅读和理解代码

  • 词不达意的命名将影响思考和注意力
  • 好代码会明确告诉你它在做什么

借鉴学习

  • 当我们看到写得很漂亮的代码时,会很受启发
  • 鼓励你把自己的代码写得更好

防止破窗

破窗效应

  • 环境中的不良现象如果被放任存在,就会诱使人们仿效,甚至变本加厉。

  • 一幢有少许破窗的建筑,如果破窗不被修理好,可能将会有破坏者破坏更多的窗户。
    最终,他们甚至会闯入建筑内,如果发现无人居住,也许就在那里定居或者纵火。

  • 一面墙,如果出现一些涂鸦而没有被清洗掉,那么很快,墙上就布满了乱七八糟、不堪入目的东西

  • 一条人行道有些许纸屑,不久后就会有更多垃圾,最终人们会视若理所当然地将垃圾顺手丢弃在地上。

  • 这个现象,就是犯罪心理学中的“破窗效应”。

如何防止

  • 任何不良现象会无线扩散,不要做“打破第一扇窗”的人
  • 整洁的代码需要每个人的精心呵护,需要整个团队都具备一些工匠精神
  • 编码前,多想想别人是否觉得你的代码容易理解
  • Code Review机制,及时发现和修复“破窗”

什么才是好的标准?

代码约少越好?

  • 单行代码:

    findId(x) >= 0 ? y * (1 + y) : y / (1 - z);
    
  • 等价代码:

    if (findId(x) >= 0) {
      y * (1 + y);
    } else {
      y / (1 - z);
    }
    

好的标准

别人理解代码所需的时间最小化

命名

命名很难

  • 表达性好的名字需要深思熟虑
  • 抽象和思考过程(人是懒惰的)
  • 找不到合适名字,说明对问题理解不够透彻

保持一致性

每个概念一个词

  • 贯穿其中
  • 常用方法名称约定
    • 新增create
    • 添加add
    • 删除remove
    • 修改update
    • 查询(单个结果)get
    • 查询(多个结果)list
    • 分页查询page
    • 统计count

使用对仗词

  • 保持对称
  • 常用组合
    • add/remove
    • increment/decrement
    • open/close
    • begin/end
    • insert/delete
    • show/hide
    • create/destroy
    • lock/unlock
    • source/target
    • first/last
    • min/max
    • start/stop
    • get/set
    • next/previous
    • up/down
    • old/new

后置限定词

  • 表示计算结果的变量,限定词置后
  • 突出主要含义
    • revenueTotal(总收入)
    • expenseTotal(总支出)
    • revenueAverage(平均收入)
    • expenseAverage(平均支出)

统一业务语言

  • 减少各方沟通成本

统一技术语言

  • 如ddd术语

为名字附带更多信息

public String start(long delay)

调整

若变量是一个度量(如时间、长度),带上它的单位。

public String start(long delaySecs)

用具体代替抽象

例1

// 检查服务是否可以监听某个指定的TCP端口
public void canStart(int port)
// 完成后触发
public void trigger()

调整

要具体,不能空泛

public void canListenOnPort(int port)

public void triggerAfterCompletion()

例2

processingData

调整

validateUserCredentials

例3

popRecord()

调整

要体现做什么,而不是怎么做

getLatestEmployee()

显化隐藏的计算过程

Matcher matcher = headerPattern.matcher(line);
if(matcher.find()){
    headers.put(matcher.group(1), matcher.group(2));
}

调整

命名有含义中间变量,显化隐藏的计算过程

Matcher matcher = headerPattern.matcher(line);
if(matcher.find()){
    String userId = matcher.group(1);
    String userName = matcher.group(2);
    headers.put(userId, userName);
}

名字应该有多长?

采用非常具体的描述性的名字?

public String newNavigationControllerWrappingViewControl()

只用单个单词或者单一字母的名字?

public String s2d()

名字应该恰如其分

在小的作用域可以使用短的名字
if (debug) {
    Map<String,Integer> m = new HashMap();
    lookUpNamesNumbers(m);
    System.out.println(m);
}
为作用域大的名字采用更长的名字
Map<String,Integer> userNameAgeMap = new HashMap();
// more code
public void someMethod() {
    lookUpNamesNumbers(userNameAgeMap);
}
缩略词
doc => document
str => string
丢掉没用的词
convertToInt() => toInt()

不会误解的名字

例1

假设有一个函数用于剪切段落的内容:

public String clip(String text, int length)

猜想

  • 从尾部删除length的长度?
  • 截掉最大程度为length的一段?

调整

public String truncate(String text, int maxLength)

例2

public static final int CART_TOO_BIG_LIMIT = 10;

CART_TOO_BIG_LIMIT的边界?

使用代码

    if(shoppingCart.size() >= CART_TOO_BIG_LIMIT){
      System.out.println("Too many items in cart.")
    }

购物车逻辑为最多不能超过10件商品

调整

    public static final int MAX_ITEMS_IN_CART = 10

给布尔值命名

boolean readPassword = true;

猜想

  • 我们需要读取密码?
  • 已经读取了密码?

调整

boolean needPassword = true;

boolean userIsAuthenticated = true;

更多

加上像is、has、can或者should,把布尔值变得更加明确

与使用者的期望想匹配

public double getMean() { 
    // Iterate through all samples
    // and return total / samplesSize
}
  • 期望中的get应该是“轻量级访问器”,可以随意调用。
  • 假如存在大量的数据,随意调用将付出很大的代价。

调整

public double computeMean()

注释

什么不需要注释?

不要为那些从代码本身就能快速推断的事实注释

// Find the Node in the given subtree,
// with the given name, using the given depth.
Node findNodeInSubtree(Node subtree, String name, int depth);
  • 不要复述功能

  • 对于这种注释,要么删除,要么改进

    // Find a Node with the given 'name' or return NULL.
    // If depth <= 0, only 'subtree' is inspected.
    // If depth == N, only 'subtree' and N levels below are inspected.
    Node findNodeInSubtree(Node subtree, String name, int depth);
    

不要给不好的名称加注释

// Releases the handle for this key. 
// This doesn't modify the actual registry.
void deleteRegistry(String key);

调整

void releaseRegistryHandle(String key);

好代码 > 坏代码 + 好注释

代码即文档

加入评论

例1

// 出乎意料的是,对于这些数据用二叉树比用哈希表快40%

// 哈希运算的代价比左/右比较大得多

例2

// 作为整体可能会丢掉几个词,这没有问题,要100%调整太难了

例3

注释也可以用来解释为什么代码写得不那么整洁:

// 这个类正在变得越来越乱

// 也许我们应该建立一个ResourceNode子类来帮助整理

为代码中的瑕疵写注释

// TODO 采用更快的算法

  • 随时把代码将来应该如何改动的思想用注释记录下来 {:&.moveIn}。

  • 这种注释给读者带来对代码质量和当前状态的宝贵见解,甚至可能会给他们指出如何改进代码的方向。

给常量加注释

public final static int NUM_THREADS = 8;

这一行看上去可能不需要注释,但很可能选用这个值的程序员需要知道得比这个要多。

调整

```java
// as long as it's >= 2 * num_processors, that's good enough.
public final static int NUM_THREADS = 8;
```

站在读者的角度

意料之中的提问

  • 当别人阅读你的代码时,有些部分更可能让他们有这样子的想法:

    什么?

    为什么会这样?

  • 你的工作就是要给这部分加上注释。

公布可能的陷阱

  • 当作为一个函数或者类写注释时,可以问自己这样的问题:

    这段代码有什么出人意料的地方?

    会不会被误用?

  • 未雨绸缪,预料到人们使用你的代码时可能会遇到的问题。

“全局观”注释

对于团队的新成员来讲,最难的事情之一就是理解“全局观”。

// 这段代码把我们的业务逻辑与数据库粘在一起。任何应用层代码都不应该直接使用它。

// 这个类看上去很复杂,但它实际上只是个奇妙的缓存。它对系统中的其他部分一无所知。

这正是那种应该包含在高级别注释中的信息。

克服“作者心理障碍”

当你对写注释犹豫不决时,

最好的办法就是直接把你心里所想写下来,

虽然这种注释可能是不成熟的:

// 哦,天啊,如果一旦这个东西在列表中有重复的话会变得很难处理的。

调整

  • 不管你心里想什么,先把它写下来
  • 读一下这段注释,看看有没有什么地方可以改进
  • 不断改进

// 小心:字段代码不会处理列表中的重复数据(因为这很难做到)

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