一、13类典型坏味道
01 | 缺乏业务含义的命名:如何精准命名?
- 不精准的命名:主要体现在命名过于宽泛,不能精准描述,这是很多代码在命名上存在的严重问题,也是代码难以理解的根源所在,所以命名要能够准确的描述出这段代码在做的事情。
- 技术术语命名:在实际的代码中,技术名词的出现,往往就代表着它缺少了一个应有的模型。在一个技术类的项目中,这些技术术语其实就是它的业务语言。但对于业务项目,这个说法就必须重新审视了。
总结:好的命名,是体现业务含义的命名。在我看来命名做到不言自喻就可,让后来者看到你的命名就大致知道这段代码的大致详情即可。
02 | 乱用英语:站在中国人的视角来看英文命名
- 违反语法规则的命名:常见的命名规则是:类名是一个名词,表示一个对象,而方法名则是一个动词,或者是动宾短语,表示一个动作。
- 不准确的英语词汇:最好的解决方案还是建立起一个业务词汇表,千万不要臆想(可以在开发前,专门开会制定业务词汇表,这样大家均用同一套词汇,更利于开发。用集体智慧,而非个体智慧。)
- 英语单词的拼写错误:常见问题,可能是粗心大意,一般开发工具会提示,但是还是会出现。
总结:编写符合英语语法规则的代码。在我看来该坏味道其实就是自身细心程度和团队开发的整体性问题,集大家之想,必将降低问题的出现。
03 | 重复代码:简单需求到处修改,怎么办?
- 复制粘贴的代码:不要使用复制粘贴。真正应该做的是,先提取出函数,然后,在需要的地方调用这个函数。复制粘贴的代码是一颗随时可能爆炸的炸弹。
- 结构重复的代码:一旦出现重复的结构,就提取公共方法,避免代码的冗余。
- if 和 else 代码块中的语句高度类似:只要你看到 if 语句出现,而且 if 和 else 的代码块长得又比较像,多半就是出现了这个坏味道
总结:不要重复自己,不要复制粘贴。
04 | 长函数:为什么你总是不可避免地写出长函数?
- 以性能为借口:性能优化不应该是写代码的第一考量。
- 把代码平铺直叙地摊在那里:关注点越多越好,粒度越小越好。
- 把多个业务处理流程放在一个函数里实现;
- 把不同层面的细节放到一个函数里实现。
- 只是每次增加了一点点:任何代码都经不起这种无意识的累积,每个人都没做错,但最终的结果很糟糕。
总结:把函数写短,越短越好。
05 | 大类:如何避免写出难以理解的大类?
如果一个类里面的内容太多,它就会超过一个人的理解范畴,顾此失彼就在所难免了。、
主要是将大类拆分成小类。我们需要认识到,模块拆分,本质上是帮助人们降低理解成本的一种方式。
- 职责不单一:把不同的职责拆分开来,我们需要对不同内容的变动原因进行分析,而支撑我们来做这种分析的就是单一职责原则。
- 字段未分组
总结:把类写小,越小越好
06 | 长参数列表:如何处理不同类型的长参数?
- 变化频率相同:则封装成一个类。
- 变化频率不同的话:
- 静态不变的,可以成为软件结构的一部分;
- 多个变化频率的,可以封装成几个类。
参数列表中经常会出现标记参数,这是参数列表变长的另一个重要原因。对于这种标记参数,一种解决方案就是根据这些标记参数,将函数拆分成多个函数。
总结:减小参数列表,越小越好
07 | 滥用控制语句:出现控制结构,多半是错误的提示
- 嵌套的代码
- else 语句:一种典型的重构手法:以卫语句取代嵌套的条件表达式,不要使用 else 关键字
- 重复的 switch:以多态取代条件表达式
- 循环语句
总结:循环和选择语句,可能都是坏味道
08 | 缺乏封装:如何应对火车代码和基本类型偏执问题?
- 过长的消息链,或者叫火车残骸
例如:
String name = book.getAuthor().getName();
解决这种代码的重构手法叫隐藏委托关系(Hide Delegate),说得更直白一些就是,把这种调用封装起来(要想摆脱初级程序员的水平,就要先从少暴露细节开始)
- 基本类型偏执:这种引入一个模型封装基本类型的重构手法,叫做以对象取代基本类型(Replace Primitive with Object)。
总结:构建模型,封装散落的代码。
09 | 可变的数据:不要让你的代码“失控”
-
满天飞的 Setter:相比于读数据,修改是一个更危险的操作。缺乏封装再加上不可控的变化,在我个人心目中,setter 几乎是排名第一的坏味道
- 消除 setter ,有一种专门的重构手法,叫做移除设值函数(Remove Setting Method)。总而言之,setter 是完全没有必要存在的。
-
可变的数据:
- 所有的字段只在构造函数中初始化;
- 所有的方法都是纯函数;
- 如果需要有改变,返回一个新的对象,而不是修改已有字段。
总结:可变数据最直白的体现就是各种 setter。setter 一方面破坏了封装,另一方面它会带来不可控的修改,给代码增添许多问题。解决它的一种方式就是移除设值函数(Remove Setting Method),将变化限制在一定的范围之内。限制可变的数据。
10 | 变量声明与赋值分离:普通的变量声明,怎么也有坏味道?
- 变量的初始化:一个变量的初始化是分成了声明和赋值两个部分,而我这里要说的就是,变量初始化最好一次性完成。尽可能使用不变的量,在能够使用 final 的地方尽量使用 final,限制变量的赋值。
- 集合初始化:传统的集合初始化方式是命令式的,而今天我们完全可以用声明式的方式进行集合的初始化,让初始化的过程一次性完成。使用java9的集合新特性
11 | 依赖混乱:你可能还没发现问题,代码就已经无法挽救了
- 缺少防腐层:会让请求对象传导到业务代码中,造成了业务与外部接口的耦合,也就是业务依赖了一个外部通信协议。一般来说,业务的稳定性要比外部接口高,这种反向的依赖就会让业务一直无法稳定下来,继而在日后带来更多的问题。解决方案自然就是引入一个防腐层,将业务和接口隔离开来。
- 业务代码中出现具体的实现类:实际上是违反了依赖倒置原则。因为违反了依赖倒置原则,业务代码也就不可避免地受到具体实现的影响,也就造成了业务代码的不稳定。识别一段代码是否属于业务,我们不妨问一下,看把它换成其它的东西,是否影响业务。解决这种坏味道就是引入一个模型,将业务与具体的实现隔离开来。
总结:代码应该向着稳定的方向依赖。
12 | 不一致的代码:为什么你的代码总被吐槽难懂?
- 命名中的不一致:表示类似含义的代码应该有一致的名字
- 方案中的不一致
- 代码中的不一致
13 | 落后的代码风格:使用“新”的语言特性和程序库升级你的代码
- Optional的使用:Optional 的引入可以减少由于程序员的忽略而引发对空对象的问题。团队内部可以约定,所有可能返回空对象的地方,都要返回 Optional,以此降低犯错的几率。
参考:https://blog.csdn.net/qq_46728644/article/details/125486253 - 函数式编程:是一个影响代码整体风格的重要编程范式
总结:不断学习“新”的代码风格,不断改善自己的代码。
学习自:https://time.geekbang.org/column/intro/100068401 如有侵权请及时联系