Angular进阶之十一:从 Rxjs 获得的启发——改良方法传入参数的形式
新版的 Rxjs 对订阅参数(Subscribe Arguments)做了一个调整。原先 subscribe() 可以传入多个参数,现在它只允许以 options 的形式传入一个参数。
import { throwError } from 'rxjs';
// deprecated
throwError('I am an error').subscribe(null, console.error);
// suggested change
throwError('I am an error').subscribe({error: console.error});
这让我想到了函数柯里化,但这两者想达到的目的不同。函数柯里化是函数式编程的基础,用以保证函数链式调用的单一职能原则。而这个将参数从 ...args 的形式改为 键值对 的目的是增强参数的语义化以及降低项目的维护难度。
这个其实很好理解,我们反过来想。如果参数从 options 变成 arg1, arg2... 我们会失去什么?
我们会失去两个有价值的东西:
- options 中 键值对 的 key 是有意义的,它赋予了 value 一个有意义的变量名,增强语义化
- options 中的值在获取的时候没有顺序的要求,离散型的参数则必须按照传入顺序获取
乍看之下这两点能起到的优化效果有限。但是当我们的项目越来越大,越来越复杂,函数的参数也会越来越多,到最后可能会出现几乎不可维护的方法。
在工作中观察到的与此相关的问题
下面我举一个例子:
this.overwriteDataSettings(settings, this.getAttributeName(), this.configOptions, undefined, 'edit_mode', { data: this.getAllData() });
这个是我在工作中见到过的一个 function,它就存在于我负责的项目里。我只调整了里面出现的变量名,其它没变。第一眼看到这个方法时我的反应是到此为止吧,不要去修改这个方法,甚至不用去看里面的逻辑,就让它安静的躺在那里就行了。
这样的方法别说维护了,能不能读懂都是个问题。就只看这一行代码,我敢保证没人能说出每个参数都起到什么作用,尤其是第四个参数还是个 undefined。我想弄懂每个参数在干什么,只能查看这个函数,一行一行的去比对它们的作用。
这对于代码维护实在是太不友好了,关键是它可能使用范围还很广。一旦改错了,半个项目的功能都会瘫痪。这样的代码到最后考验的已经不是技术了,而是勇气。
再举一个极端的例子:
initData() {
this.methodOne(arg1, undefined, undefined, undefined, arg5);
this.methodTwo(arg1, undefined, arg3, arg4, undefined, arg6);
this.methodThree(undefined, arg2, undefined, arg4);
}
万幸我没遇到过这样的代码,但是我看见过类似的。这都不用考虑维护了,找机会直接重构吧。
通过观察和总结,我们不难发现这样的函数往往还伴随着其它的问题。最严重的就是违背了单一职能原则。我敢保证像这样的函数里面一定是一堆的 if...else...,也就意味着它做了很多不该它做的事情。
程序设计原则是一个老生常谈的话题了,它很容易被忽视,而当我们想到它的时候往往已经是积重难返了。
预防此类问题的措施
我总结了一些能预防对此类问题的措施:
- 函数名称和它的功能匹配
- 参数的数量不要超过 2 个
- 每个函数的长度限制在 20或30 行以内
我们都希望打出来的代码符合 程序设计原则 的规范。可是当我们面对实际项目,面对复杂的功能时,这些原则又很容易被遗漏。究其原因,我认为是这些理论知识抽象程度太高,导致我们很难将它们映射到现实中来。就好像方位感不好的人就算是照着地图都找不准正确的地址。更别说连图形都没有,只有文字表述的概念了。
假设我们能制定一些详细的、明确的规范,比如上面提到的三点,我们就能把抽象的东西具象化。这三点只字没提设计原则,但是写出来的代码却是符合原则的。我觉得每一个开发团队都应当维护一个代码规范列表。这个列表的内容都是具体的要求,禁止...,允许...。那么这就实现了对设计原则的一种量化,让它成为可以直观去考察的东西。而且这也是一种团队文化,能加强团队凝聚力。
总结
Rxjs 这一次对传参的优化是保证代码可读,可维护的一次努力。技术不是一蹴而就的,它需要不断地改良。就是这样一次次的努力,提升了代码的效率,保证了项目的健康发展。
相关链接: