代码的坏味道笔记
在 VUCA 时代,我们写代码的方式也应随时代的变化而变化,不一直拘泥于以往也许被当作最佳实践的方式。
万象丛生
现象:我们也许缺少的是“实现模式”而不是“设计模式”
并没有否定设计模式在编程中的重要作用,但是很多人“手里拿着锤子,眼中满世界都是钉子”。希望所有的程序都能套用一下设计模式,过犹不及。相较于设计模式的抽象,其实我们更需要的是日程编程中的实现模式。如何将每一行代码写好比起如何用设计模式来的更直接。
现象:很多重复代码的产生通常都是从程序员偷懒开始的
不管是需求的快速变更,还是领导带给的压力。很多程序员以“快”为借口,写出了越来越多的重复代码,为后续工作埋下更多地隐患,真正的“欲速而不达”。这正如同“破窗理论”,前人以这种方式写代码,后人便会肆无忌惮的追求所谓的“快”,代码便变得越来越不可控。
现象:面试造轮子,工作拧螺丝
比如复杂的分布式知识,这东西却成了主流。这与大公司面试方式有关。记得当年微软关于智力的面试题,使得一部分程序员在面试之前都要做智力面试的储备。但这真的有用吗?却是值得商榷的。微软后来发现,这种选拔人才的方式是有问题的。但是国内的面试仍然在做着一样的事情。
毕竟,有大量用户规模的事,只有一些大公司的核心团队才会遇到,而大部分人需要的是,写好代码。而大部分人学习分布式只是学习屠龙术,空有一身本事,无处施展。
现象:大部分人都丢弃了学生时代的“受控练习”
每天都保持学习的重要性不必赘言。但是我们回头想想,我们单位时间掌握知识密度最大的时期当属学生时期。原因就在于我们那时候做了大量的“受控练习”。缺乏在受控环境下的刻意练习,很难通过工作中的自然积累提升判断力。
对正确的代码构造足够熟悉,也是很重要的一个基本功。“正确的代码构造”并非无穷无尽,实际上在单线程编程中,几十个常见的模式已经几乎能够完全覆盖所有场景。因此其实只需要很少的时间对这些基本功做“受控练习”便能掌握。
代码的坏味道
命名过于宽泛,不能精准描述
这是很多代码在命名上存在的严重问题,也是代码难以理解的根源所在。一个好的名字应该描述意图,而非细节。类名是一个名词,表示一个对象,而方法名则是一个动词,或者是动宾短语,表示一个动作。
命名中技术名词的出现
在实际的代码中,技术名词的出现,往往就代表着它缺少了一个应有的模型。编写可维护的代码要使用业务语言。其次,要建立团队词汇表。
长函数的出现
以性能为由,或者每次增加一点点代码出现的长函数是一种典型的坏味道。我们要定义好函数长度的标准,并且做好关注点分离。
长参数的出现
应该将参数列表封装成对象或者是模型传递给函数。如果是标记参数导致的长参数,应该将函数拆成多个函数。
大类的出现
指责不单一和字段未分组是产生大类的最主要原因。因此,关键就是能够把类的不同的职责拆分开来。
如果我们把大类都拆成小类,类的数量就会增多,那人们理解的成本是不是也会增加呢?
- 我们会看到在各种程序设计语言中,有诸如包、命名空间之类的机制,将各种类组合在一起。在你不需要展开细节时,面对的是一个类的集合。
- 再进一步,还有各种程序库把这些打包出来的东西再进一步打包,让我们只要面对简单的接口,而不必关心各种细节。
- 你最先关注的一定是主线,在主线的基础上进行思维和理解的扩展。
- 类多不是问题,问题是过度设计造成的难以理解的结构才是问题。
不建议使用 for 循环,应该使用函数式编程比如 map
for 循环里面如果有判断,要将 for 循环里面的方法提炼成方法,这样可以减少锁进,进一步提高代码的阅读性。
采用列表转换代替写出来的代码相较于传统的循环语句 for 写出来的代码,表达性更好,因为它们都是描述做什么,而传统的循环语句是在描述怎么做。
尽量不用 else
以卫语句取代嵌套的条件表达式(Replace Nested Conditional with Guard Clauses)
根据业务移除 setter
可变数据最直白的体现就是各种 setter。setter 一方面破坏了封装,另一方面它会带来不可控的修改,给代码增添许多问题。解决它的一种方式就是移除设值函数(Remove Setting Method),将变化限制在一定的范围之内。
变量的声明和赋值是分离的
变量的初始化包含变量的声明和赋值两个部分,一个编程的原则是“变量要一次性完成初始化”。
二者分离带来的问题就是,把赋值的过程与业务处理混杂在一起。发现变量声明与赋值分离一个做法就是在声明前面加上 final,用“不变性”约束代码。
传统的集合初始化方式是命令式的,而今天我们完全可以用声明式的方式进行集合的初始化,让初始化的过程一次性完成。再进一步,以声明式的标准来看代码,会帮助我们发现许多的坏味道。
过长的消息链(Train Wreck)
重构的手法是隐藏委托关系,通过单独定义方法的方式隐藏在消息链中要返回的模型对象。
基本类型偏执(Primitive Obsession)
解决办法是引入一个模型。
工程师鸡汤
重复是一个泥潭,对于程序员来说,时刻提醒自己不要重复是至关重要的。在软件开发里,有一个重要的原则叫做 Don’t Repeat Yourself(不要重复自己,简称 DRY)
有了对于软件开发更多的思考,回过头再来写代码时,我就能看到更多的维度,能意识到自己在写的代码对他人和未来的影响,这时自然会尽力把自己的代码写得更整洁。
一位“知行合一”的程序员最终会发现,极限编程是唯一合理且有效的软件开发方法
在一个系统中,每一处知识都必须有单一、明确、权威地表述。Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
不要复制粘贴。如果需要复制粘贴,首先应该做的是提取一个新的函数出来,把公共的部分先统一掉。
谨慎地对待接口和实体的变动
一个好的封装应该是基于行为的
代码评审推衍到极致就是结对编程,以下是代码评审的角度
- 实现方案的正确性
- 算法的正确性
- 代码的坏味道