代码整洁之道-参数检查和 null

原创文章,转载请注明出处

引言
代码质量的好坏,本身是一个比较难量化的标准,现在应该很少有公司再以一个程序员产出的代码行数作为标准了。怎样来评判代码的好坏其实是一项比较麻烦的事情,每个人的着眼点不同,相应的代码就各式各样。但是根据自身经验来看,当然也是我比较信奉的一点,就是在目前的开发条件下,代码的组织结构是比执行效率需要优先考虑的事情。

本篇的主旨是整理一些在开发中能够实际提升代码质量的工具和技巧。

Apache-Commons和Guava

相信有过一些开发经验的人对这两个工具都不会太陌生,他们提供的许多类能够大大提高开发效率,并且使代码更加整洁。个人更喜欢 Guava 的设计,工作中往往是混用,当然要理解每种工具的适用场景,才能做到高效和简洁。

1. 参数检查

刚参加工作的时候,印象非常深刻的是当我想检查一些参数是否符合要求时,要写许多冗余的代码。
例如在一个 Service 的入口处想要检查一个 String 类型的参数是否为空,这是我最初的写法:

public void doSomething(String str) throws Exception {
    if (str == null || str.length() == 0) {
        throw new Exception("str should not be blank");
    }
}

乍看之下确实没有什么问题,但是当参数个数增加的时候事情就变得麻烦了,同样的代码逻辑需要重复好几遍,最致命的就是导致可读性下降。还有就是当str是连续的空格时也绕过了检查。
Guava中的 Preconditions类就是专门解决参数检查问题的,先来看看使用了 Preconditions 以后的代码变成了什么样子:

public void doSomething(String str) throws Exception {
    Preconditions.checkArgument(StringUtils.isNotBlank(str), "str should not be blank");
}

很多教程中都强烈建议将 Preconditions 静态导入,这样代码会更加简洁。通过比较两处代码不难看出:

  1. 代码的可读性提高了
  2. 替代掉了 ifthrow 语句,提升了可维护性
  3. 省略了判断条件,代码更健壮

关于2,3点可能需要说明一下。记得 Boss 之前有跟我聊过一次,有一点是说自己写的代码里应该尽量少出现 if 这样的控制结构,当时理解并不是太深刻,但是随着写的代码越来越多,渐渐意识到这其实是思想的一种转变。
在一个方法之中,出现的控制结构越多,思维还停留在面向过程编程的可能性就越大(虽然现在大多数程序员都不愿意承认这点)。就以上面的代码为例,判断字符串是否为空的这项功能并不是方法的主要业务,那么就应该由专门做这项功能的类(对象)来进行处理,所以我们交给了 StringUtils ,同样的,根据参数是否满足要求来抛出异常的功能我们交给了 Preconditions
顺带值得一提的是 Preconditions 中还有许多检查参数的方法:checkNotNull(), checkState()等等,这些方法都是快速失败的。

2. 思考 null 想表达的意义

每次自己写的程序报了空指针错误,我都会提醒自己更加谨慎,因为这其实是在说:
你考虑的不够周全
许多时候NPE 所代表的问题是我们并没有思考清楚 null在这里是想表达什么意思,还是先看一个例子:
我们现在想从一个 List<Map<String, String>>结构的链表中逐个解析每个字段,他的数据大概是这个样子:

[
    {
        "code":"123",
        "name":"yzhang",
        "nickname":"Rocket"
    },
    {
        "code":"234",
        "name":"Kevin",
        "nickname":"Bee"
    }
]

但是现在有一些规则:

  1. 每个 map 中的 code 不能为空,否则跳过
  2. name 可以为空,但是不能显示为“null”
  3. nickname 如果为空,则使用默认值“Avenger”

下面是一段实现代码:

for (int i = 0, size = personList.size(); i < size; i++) {
    Map<String, String> person = personList.get(i);
    if (MapUtils.isNotEmpty(person)) {
        String code = person.get("code");
        String name = person.get("name");
        String nickname = person.get("nickname");
        if (StringUtils.isBlank(code)) continue;
        if (name == null) name = "";
        if (nickname == null) nickname = "Avenger";
        // do something else
    }
}

这段代码已经考虑了一些可能出现 NPE 的情况,但是出现了和 [1] 中相同的问题,有大量的if语句,对于后期维护非常不利。
Guava 中提供了Optional类来强制开发人员思考 null所表达的意义,正如 Optional 表达的意思,当一个变量可能会出现 NPE 时我们就应该用 Optional 来处理它(Java 8的util包已经加入了 Optional 类,接口命名上有稍许不同)
看看改进后的代码:

for (int i = 0, size = personList.size(); i < size; i++) {
    Map<String, String> person = Optional.fromNullable(personList.get(i)).or(Maps.newHashMap());
    String code = person.get("code");
    if (StringUtils.isBlank(code)) continue;
    String nickname = Optional.fromNullable(person.get("nickname")).or("Avenger");
    String name = Optional.fromNullable(person.get("name")).or("");
    // do something else
}

同样的,我们减少了很多 if结构,代码也更便于阅读(比起与一堆null做比较,fromNullableor 显然更清晰)。
还有很重要的一点,即使在第一段代码中,也有不少人会忘记写if (MapUtils.isNotEmpty(person)),因为这没有在三点要求以内。
关于 Optional的详细使用可以参考 Guava 的官方文档,值得注意的是Optional.of(T t)也是快速失败的。建议在一个对象有不确定性的时候都考虑使用 Optional 来处理,特别是在遍历 MapList 之类的集合时,因为你永远不知道调用者什么时候会给你一个null

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