再读编程的智慧一文有感
记得是2015年还是2016年读过王垠的这篇文章--编程的智慧,收益良多。一些当时让我影响深刻的点,自那以后就一直用于生产了。
今天重温此文,感触最深的有以下三个点:
- 循环时避免使用continue和break,并提供了如何去掉的方法
- 如何正确的处理Java里的异常和错误
- 如何正确的处理Java里的空指针
本文是编程的智慧一文的内容概要。当然最好的方式,是花上2到3小时认真阅读王垠的原文,相信你会有很多感触和收获。
正文内容
推敲代码
反复推敲代码,仔细领悟,积累灵感和智慧,为了在遇到新问题的时候直接朝正确,或者接近正确的方向前进。
优雅代码的形状特征
看起来像整整齐齐,套在一起的盒子。抽屉里分门别类,再放几个小盒子。
逻辑上是枝丫分明的树状结构。程序所做的几乎一切事情,都是信息的传递和分支。
模块化代码
模块化不是简单的把代码切割开来,分放在不同的位置。指的是逻辑上模块化。
需要做到以下几点:
- 一个函数不超过40行
- 制造小的工具函数。函数内联,内联扩展是用来消除函数调用时的时间开销。它通常用于频繁执行的函数,对于小内存空间的函数非常受益
- 每个函数只做一件简单的事
- 避免使用全局变量和类成员来传递信息
可读的代码
使用有意义的函数和变量
局部变量应该尽量接近使用它的地方,加强读者对于这里的“计算顺序”的理解,局部变量的本质——它们就是电路里的导线。
局部变量名字应该简短
不要重用局部变量,否则会把局部变量的作用域不必要的增大。
把复杂逻辑提取出来,做成帮助函数
把复杂的表达式提取出来,做成中间变量
合理的地方换行
简单的代码
程序语言都喜欢标新立异,提供这样那样的“特性”,然而有些特性其实并不是什么好东西。很多特性都经不起时间的考验,最后带来的麻烦,比解决的问题还多。很多人盲目的追求“短小”和“精悍”,或者为了显示自己头脑聪明,学得快,所以喜欢利用语言里的一些特殊构造,写出过于“聪明”,难以理解的代码。
并不是语言提供什么,你就一定要把它用上的。实际上你只需要其中很小的一部分功能,就能写出优秀的代码。
避免使用自增减表达式(i++,++i,i–,–i)用在复杂的表达式里面
永远不要省略花括号
合理使用括号,不要盲目依赖操作符优先级,能降低视觉疲劳。
避免使用continue和break。这点更新了我的认知。它们依靠“控制流”来描述“不做什么”,“跳过什么”,结果到最后你也没搞清楚它到底“要做什么”。
- 如果出现了continue,你往往只需要把continue的条件反向,就可以消除continue。
- 如果出现了break,你往往可以把break的条件,合并到循环头部的终止条件里,从而去掉break。
- 有时候你可以把break替换成return,从而去掉break。
- 如果以上都失败了,你也许可以把循环里面复杂的部分提取出来,做成函数调用,之后continue或者break就可以去掉了。
写直观的代码
如果有更加直接,更加清晰的写法,就选择它,即使它看起来更长,更笨,也一样选择它
写无懈可击的代码
处理所有可能出现的情况,避免漏掉corner case。if语句就要把else情况都写上。
正确处理异常
Java的函数如果出现问题,一般通过异常(exception)来表示。你可以把异常加上函数本来的返回值,看成是一个“union类型”。
- catch异常的时候,你不应该使用Exception这么宽泛的类型或者。你应该正好catch可能发生的那种异常A。使用宽泛的异常类型有很大的问题,因为它会不经意的catch住另外的异常(比如B)
- 在自己函数的类型加上throws Exception,那么你就不可避免的需要在调用它的地方处理这个异常,如果调用它的函数也写着throws Exception,这毛病就传得更远
- try { … } catch里面,应该包含尽量少的代码,一个try里包含做一件事的逻辑代码
正确处理null指针
尽量不要产生null指针。尽量不要用null来初始化变量,函数尽量不要返回null。如果你的函数要返回“没有”,“出错了”之类的结果,尽量使用Java的异常机制
不要catch NullPointerException,而应该是在角色函数里处理过滤空指针
不要把null放进“容器数据结构”里面
其实是强硬的态度。你要告诉函数的使用者,我的参数全都不能是null,如果你给我null,程序崩溃了该你自己负责,采用强硬态度一个很简单的做法是使用Objects.requireNonNull()
使用@NotNull和@Nullable标记,它们的作用是:IntelliJ本身会对含有这种标记的代码进行静态分析,指出运行时可能出现NullPointerException的地方。在运行时,会在null指针不该出现的地方产生IllegalArgumentException
使用Optional类型,设计原理,就是把“检查”和“访问”这两个操作合二为一,成为一个“原子操作”。
附带:Java里closure的自由变量是只读的。所以内部类里的函数可以读外部的局部变量,但是不能对其写操作。
过度工程的征兆
- 就是当你过度的思考“将来”
- 过度的关心“代码重用”
- 过度地关心“测试”
小结
如果对上面的概述若有所思,有更多详细的细节在原文里-编程的智慧