《代码整洁之道》读书笔记(二)之什么样的函数是好函数

什么是好的函数

1. 短小

函数的第一规则是要短小。第二条规则是还要更短小。

函数二十行最佳。
函数的缩进层级不应该超过二层。(这个有点难

2. 只做一件事

函数应该做一件事。做好这件事。只做这件事。

3. 每个函数一个抽象层级

要确保函数只做一件事,函数中的语句都要在同一个抽象层级上。

函数中混杂不同抽象层级,让人迷惑。读者可能无法判断某个表达式是基础概念还是细节。更恶劣的是,就像破损的窗户,一旦细节与基础概念混杂,更多的细节就会在函数中纠结起来。

4. switch语句不可避免,使用多态隐藏到较低的抽象层

switch语句天生就是要做N件事,不幸地是我们总无法避开,不过还是能够确保每个switch语句都埋藏在较低的抽象层,而且永远不重复。

5. 使用描述性的名称

花时间给函数取个好的具有描述性的名字是很值得的,不要害怕长名字。函数功能越集中、越短小越容易取名字,如果你发现你对你的函数很难取名字的时候,是该考虑这个函数是否需要进行拆分了。

6. 函数参数

最理想的参数数量是零,其次是一,再次是二,应尽量避免三。有足够特殊的理由才能用三个以上参数——所以无论如何也不要这么做。

函数尽量不要有标识参数。向函数传入boolean值简直就是骇人听闻的做法。

如果函数看起来需要两个、三个或三个以上的参数,就说明其中一些参数应该封装为类了。例如,下面两个声明的差别:

Circle makeCircle(double x, double y, double radius);
Circle makeCircle(Point center, double radius);

给函数取个好名字,能较好地解释函数的意图,以及参数的顺序和意图。对于一元函数,函数和参数应当形成一种非常良好的动词/名词对形式。
例如:

write(name);        // 不管name是什么,都要被write
writeField(name);   // 它告诉读者name是一个Field

7. 无副作用

副作用是一种谎言。函数承诺只做一件事,但还是会做其他被藏起来的事。

输出参数

应该避免使用输出参数。如果函数必须要修改某种状态,就修改所属对象的状态吧。

用作输出而非输入的参数,让人迷惑,例如:

appendFooter(s);

这个函数是把s添加到什么后面吗?或者它把什么东西添加到了s后面? s是输入参数还是输出参数? 稍微花点时间看一下函数签名:

public void appendFooter(StringBuilder report);

事情清楚了,但付出了检查函数签名的代价。应该避免这种中断思路的事。

在面向对象语言中对输出参数的大部分需求已经消失了,因为this也有输出参数的意味在内。换言之,最好是这样调用appendFooter:

report.appendFooter();

8. 使用异常代替返回错误码

返回错误码可能导致更深层次的嵌套结构。当返回错误码时就是在要求调用者立即处理错误。如果使用异常代替返回错误码,错误处理代码就能从主路径代码中分离出来,得到简化。

// 嵌套结构层次深
if (deletePage(page) == E_OK) {
    if (registry.deleteReference(page.name) == E_OK) {
        if (configKeys.deleteKey(page.name.makeKey()) == E_OK) {
            logger.log("page deleted");
        } else {
            logger.log(configKey not deleted);
        }
    } else {
        logger.log("deleteReference from registry failed");
    }
} else {
    logger.log("deleted failed");
    return E_ERROR;
}

//  使用异常处理代替返回错误码
try {
    deletePage(page);
    registry.deleteReference(page.name);
    configKeys.deleteKey(page.name.makeKey());
} catch (Exception e) {
    logger.log(e.getMessage());
}

抽离try/catch代码块

try/catch代码块丑陋不堪。它们搞乱了代码结构,把错误处理与正常流程混为一谈。最好把try和catch代码块的主体部分抽离出来,另外形成函数。

public void delete(Page page) {
    try {
        deletePageAndAllReferences(page);
    } catch (Exception e) {
        logError(e);
    }
}

private void deletePageAndAllReferences(Page page) throws Exception {
    deletePage(page);
    registry.deleteReference(page.name);
    configKeys.deleteKey(page.name.makeKey());
}

private void logError(Exception e) {
    logger.log(e.getMessage());
}

错误处理就是一件事

函数应该只做一件事。错误处理就是一件事。因此,处理错误的函数不该做其他事。意味着如果关键字try在某个函数中存在,它就该是这个函数的第一个单词,而且在catch/finaly代码块后面也不该有其他内容。(PS:好严格)

Error.java 依赖磁铁

返回错误码通常意味着某处有个类或是枚举,定义了所有错误码。

public enum Error {
    OK,
    INVALID,
    NO_SUCH,
    LOCKED,
    OUT_OF_RESOURCES,
    WAITING_FOR_EVENT,
}

这样的类就是一块依赖磁铁,其他许多类都得导入和使用它。当Error枚举修改时,所有这些其他类都需要重新编译和部署。

使用异常替代错误码,新异常就可以从异常类派生出来,无需重新编译或重新部署。

9. 别重复自己(DRY原则)

重复可能是软件中一切邪恶的根源。许多原则与实践规则都是为了控制与消除重复而创建的。

10. 结构化编程

Dijkstra认为,每个函数、函数中的每个代码块都应该有一个入口、一个出口。遵循这些规则,意味着在每个函数中只该有一个return语句,循环中不能有break或continue语句,而且永永远远不能有任何goto语句。

这个规范对于小函数帮助不大,只有在大函数中才有明显的好处。

11. 如何写出这样的函数

跟写文章一样,先想什么就写什么,然后不断打磨。初稿也许粗陋无序,你就斟酌推敲,直至达到你心目中的样子。

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

推荐阅读更多精彩内容