不要再返回Null了

背景

最近在做公司系统的模块化重构,发现了大量的函数和方法会在一定情况下返回一个Null值。虽然我能理解当时作者一定是想告诉我们此函数或方法在一定条件下返回空值。但是真的使用返回Null的方式来表达返回空值真的合适吗?

知识回顾

先从语意上理解什么是Null?

我从WIKIpedia上选了靠前的、编程角度上来说关于Null的解释。

Null 是一特殊指针值(或是一种 对象引用)表示这个指针并不指向任何的对象。这样的指针称之为 Null指针[1]            
                                    
                                                                       --WIKIpedia

计算机专业出身的同学应该能够很好理解这段话的意思。意思是说Null的类型是指针(因为它是Null指针)。

在许多定义里,Null 意指 "没有值" 或是 "未知的值"。  
                                                                --WIKIpedia

这段话则说明了Null在语意上的模糊性。既可能表示“没有值”,也可能表示“未知的值”。

为什么不要返回Null

  1. 语意的角度

先看一段代码。这段代码使用了React 16 里面的新特性。我先不说这段代码的意思,我们尝试只看代码猜一猜这段执行这段代码以后会发生什么事情。

const MAX_PIZZAS = 20;

function addAnotherPizza(state, props) {
  if (state.pizza === MAX_PIZZAS) {
    return null;
  }

  return {
    pizza: state.pizza + 1,
  }
}

this.setState(addAnotherPizza);

在上面的代码里面, 我们可以知道作者定义了一个常数MAX_PIZZAS,和一个函数 addAnotherPizza,然后执行 React.Component的setState方法。

好,读方法名字的时候,我们大概可以猜测到这是一个更新披萨数量的的方法。直意过来就是“添加另一个披萨”。直接看这个函数的返回值,可以知道这个方法返回了一个对象(新的state),这个新的对象更新了pizza的数量(其实这种命名也不好,应该表明是numOfPizza)。但是我们也发现,当pizza数量达到一个上限的时候,就会返回一个null。

OK。想象一下你是一个新来这个项目的员工,你来猜测一下这里返回一个null代表了什么意思。你可能会说,那肯定就是不再添加pizza的数量咯。对一半吧。那为什么返回null而不返回空对象呢?

在揭晓正确答案之前,首先说一下什么是语意。我理解的语意就是你写的变量名,函数名,方法名,类名,接口名等等有意义。

在语言学上,指发出讯息者想要表示或传达给发现者或接收者的理念;
                                                    --wikipedia

也就是说我们编写的代码不能只有机器能够理解,以后接我们工作的同学也能轻易地理解我们的代码到底想表达什么意义。

把上面的的函数翻译成人话的话,就是:

    A: 再加一个披萨 ( 函数名: addAnotherPizza)
    B: 当前不足20个,好的,成功加一个披萨
    ...
    A: 再加一个披萨
    B:当前已经够20个。那么, null。

你能够理解吗?

回到上面的代码。React 16版本里面setState接收null值来明确告诉React不要渲染。所以除了不再添加一个披萨到新的state以外,还有一个意思就是告诉React不要做渲染了。

至少我作为一个新的代码维护者的角度来说的话,我不能轻易地、明确地判断出当addAnotherPizza返回null时,到底想表达什么。

  1. 防范NPE的角度 (NullPointerException)

有JAVA背景的同学,应该对于NPE一点也不陌生。NPE应该是伴随了我们很多的开发时间。NPE主要发生在调用一个对象的方法时,检测到应该有的对象引用的值是null。

所以为了防范NullPointerException, 我们为我们的方法和函数添加了数不清的null 检查逻辑。使用null值检查逻辑防范NullPointerException是一个很好的编程习惯。这可以增强系统的健壮性。

但是,如果你仔细观察我们的代码,可以发现很多的null检查是不必要的。特别是对于一些自建系统的接口函数调用。举个如下的列子:

class Member {
    private String phoneNumber;
    private String memberId;

    public String getPhoneNumber(String memberId){
        if ( memberId == this.memberId ) {
            return this.phoneNumber;
        } else {
            return null
        }
    }
}

相信大家在很多的代码里面看见过类似的逻辑。即当成功时返回某项值,反之则返回null。

从语意的角度上说的话,你找某个管理员去询问某个member的电话号码,得到null的回馈时,你会怎么想?是去想这个member没有电话号码呢?还是没有这个member,还是这个管理员傻了?

除了语意上的模凌两可以外,在一个系统里面返回过多的null,也在一定程度上增加了null的检查逻辑代码数量,其实就是给系统增加了不必要的复杂性。所以,如果在某些情况下,比如上述返回电话号码的情况下,为何不返回一个空字符串呢?

从防范NPE的角度来看,返回空字符串的话,首先,getPhoneNumber的调用者是不需要进行null检查的。因为你不返回null,那为什么还要进行null检查?其次,主动不去返回null还有一个好处,就是我们作为一个有素养的程序员和工程师,主动从系统健壮性的角度防范了NPE的产生,从源头上保护了NPE对系统带来的危害。

不给系统留下任何健壮性危害隐患,是作为一个有素养的程序员应该有的觉悟和编程习惯。作为一个有素养的程序员,永远不要指望Debug来让我们的系统健壮,而是时时刻刻地去主动考虑如何才能保护我们的系统不因为我们技术的原因而崩塌。

  1. 面向对象编程的角度

在从面相对象编程的角度来展开讨论之前,我们有必要知道什么是面相对象编程。

Object-oriented programming (OOP) is a programming paradigm based on the concept of "objects", which may contain data, in the form of fields, often known as attributes; and code, in the form of procedures, often known as methods.
                                                    --wikipedia

说白一点就是在系统里一切皆对象,对象包含了一切必要的属性和属性操作方法。

那么我们的系统就是由对象和对象的关系构成的。在上面的知识回顾当中,我们可以知道null是一个指针类型的值。表示该指针不指向任何的对象。从一个系统抽象的角度来看,指针类型属于比较底层的类型。那么在我们的很多方法当中去返回一个底层的类型值是否合适呢?

举个简单的列子,我们人是由细胞组成器官然后再组成我们人这个类型的。如果每一个细胞代表了一个实例,那么每个实例所在的位置则由指针的值保存(实例内存地址)。我们人这个系统中,细胞和细胞有关系,器官与器官有关系,器官与细胞有关系。他们互相有数据的来往,有数据的处理,但是唯独没有的是,当某个器官想查询某个细胞状态时,相应器官返回一个空位置(null)的指针。因为这样既没有意义,也没有任何人体内部器官会去做这个事情。

人体是一个非常健壮和复杂的系统,我们可以通过观察我们人体自身的系统来获取编程的灵感。

null在我们的编程过程中,很多时候被误用为表明想要查找的对象不存在。如果这么想的话,只能说明你还离OOP还有一段距离。因为你还在用机器的思维在思考编写代码。如果严格按照OOP来思考如何构建我们系统的话,我们就会有更好的方法去表明一个不存在的对象(Clean Code: Special Case Pattern)。

业界相关讨论

以上都是我自己对返回null值的思考和总结。在业界上,早就有相关是否返回null值有激烈的讨论。没有讨论出业界一致公认的标准。

在Stackoverflow上关于是否返回null的讨论: Is returning null bad design?

Null之父Tony Hoare关于Null值的文章: Null References: The billion dollar mistakes

Clean Code 作者 Robert C. Martin 关于是否返回null值的看法是 “Avoid returning null”.

其他一些作者关于是否返回null的一些讨论: Why NULL is Bad?

总结

本文认为Null值不应该由程序员在方法中返回,因为这样做,一是违背了方法名的语意。二是由于我们返回null值,更加增加了系统的不稳定性;因为在调用者忘记做null检查时,就一定会因为我们的方法出错。最后,null值所充斥的系统模型违反了OOP原则。

所以,本文认为,在我们构建的系统中,不应该有返回null值的方法和函数存在。

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

推荐阅读更多精彩内容

  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,114评论 0 13
  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,300评论 8 265
  • 1.你知道吗?太阳每一天升起的方位都不一样。如果你先吃下去,你就会发现。日出日落并不是简单的东升西落。 2.缓坡,...
    夜寂日志阅读 275评论 0 0
  • 如果挣断绳索,我该去哪儿? 远处,有无数的地方可去;却又都不能去… 我不知道,为什么每每温热残余,落霞斜挂的时候,...
    左菊秋华阅读 295评论 0 1
  • 1 早上我妹妹来找我,她拿着一本很厚的区块链书,说看了几页就不想看了,实在是晦涩难懂,问我到底区块链是什么、比特币...
    猫晓语阅读 298评论 0 0