你代码的异味是故意的还是不小心?是故意的!

敏捷开发
• 阅读 284

你代码的异味是故意的还是不小心?是故意的!

一、代码竟会有“气味”

食物在腐烂之际,会散发出异味,提醒人食物已经坏掉了,需要处理。同样,如果代码中某处出现了问题,也会有一些症状。这些症状,被称之为“代码异味”(Code smell,也译作“代码味道”)。与食物腐败发出的味道不同的是,代码异味并非真正的气味,而是一种“暗示”,暗示我们的代码可能有问题,提示程序员需要对项目设计进行更进一步的查看。

代码异味一词最初是由Kent Beck在帮助Martin Fowler在编写《重构:改善既有代码的设计》一书时创造的。Martin Fowler对代码异味的定义是:代码异味是一种表象,它通常对应于系统中更深层次的问题。

代码异味的产生原因跟厨师的“清洗过程中故意保留”不一样,它更多地并非刻意为之,创造者也未必“品尝”过自己所写的代码,它更多地是由于设计缺陷或不良编码习惯而导致的不良代码症状。

这种异味也并非来自一种有据可查的标准,更多的是来自程序员的直觉。尤其是经验丰富和知识渊博的程序员,他们无需思考,只要通过查看代码或一段设计就可以立马对这个代码质量产生这种“感觉”,能对代码设计的优劣有一个大致的判断。这有点类似我们英语学到一定程度后,即便不能完全看懂文章,但凭借语感也能选出正确答案。

二、代码异味的影响

对于代码异味的出现我们其实无需过度紧张,因为在整个程序中代码异味是无处不在的

一般情况下,有“异味”的代码也依旧能运行得很好。只是倘若重视不够,没有适当地维护或改进代码,代码质量就会下降,系统也会开始变得难以维护和扩展,同时也会增加技术债务。这就像做出有异味的九转大肠的的小胖厨师,在前期准备中对评委的建议置若罔闻,一意孤行,做出来的菜连自己都难以下咽。

所以团队应尽可能地做有质量的代码,减少甚至避免这些问题,产生高效益的成果。

三、如何辨别代码异味

代码是否存在代码异味,通常是靠程序员的主观判断,但由于语言、开发者、开发理论的不同,对代码异味的判断也会存在差异。

所以要想更精准地识别代码异味,获得更高的代码质量,程序员需要大量的实践和经验。不过,前辈们总结的经验也可以让我们少走一些弯路。Martin Fowler在《重构:改善既有代码的设计》一书中,列举了最常见的24种代码异味,可以帮助我们轻松识别,便于处理和改善它们:

1) 过大的类(Large Class)

一个类包含许多字段、方法或者代码行,并逐渐变得臃肿。

2) 数据泥团(Data Clumps)

代码的不同部分包含了相同的变量组,且这些数据总是绑在一起出现。

3) 过长参数列表(Long Parameter List)

指一个方法的参数超过了三个或四个。出现这种情况一般是将几种类型的算法合并到一个方法之后。

4) 基本类型偏执(Primitive Obsession)

创建一个原始字段比创建一个全新的类要容易得多,所以对于具有意义的业务概念如钱、坐标、范围等,很多程序员不愿意进行建模,而是使用基本数据类型进行表示,进而导致代码内聚性差、可读性差。

5) 神秘命名(Mysterious Name)

在编程中,命名是一件非常恼人的事情。一些可能只有自己看懂的命名,无疑加大了代码可读性的难度,有时甚至自己也会忘记这些命名的含义。

6) 重复代码(Duplicated Code)

这几乎是最常见的异味。当多个程序员同时处理同一程序的不同部分时,通常会发生这种情况。

7) 过长的函数(Long Function)

根据Martin Fowler的经验,通常活得最长、最好的程序,其中的函数都比较短。函数越长,就越难理解。

8) 全局数据(Global Data)

这是一个非常可怕且刺鼻的异味代码。因为从代码库的任何一个角落都可以修改它,而且没有任何机制可以探测出到底是哪段代码做出了修改。全局数据造成一次又一次的诡异Bug,让我们很难找出出错的代码。

9) 可变数据(Mutable Data)

如果可变数据的变量的作用域越大, 越容易出现问题。变量是可以更改的,但我们可能不知道是哪里改变了它。

10) 发散式变化(Divergent Change)

是指一个类受到多种变化的影响。

11) 霰弹式修改(Shotgun Surgery)

是指一种变化引发多个类相应修改。

12) 依恋情结(Feature Envy)

一个类使用另一个类的内部字段和方法的数据多于它自己的数据。

13) 重复的Switch(Repeated Switch)

在不同的地方反复使用switch逻辑。这带来的问题就是当我们想要增加一个选择分支时,就必须找到所有的switch,并逐一更新。

14) 循环语句(Loops)

在编程语言中,循环一直是程序设计的核心要素。在《重构》中,Martin Fowler认为它是一种代码异味,因为他们觉得如今的循环已经有点过时了。他们提出“以管道取代循环”,这样可以帮助我们更快看清被处理的元素以及处理它们的动作。

15) 冗赘的元素(Lazy Element)

这是几乎无用的组件。我们在设计代码时有时为了未来的功能设计出“预备”代码,但实际上从未实现;又或者这个类本来有用但随着重构,越来越小,最后只剩下一个函数。无论哪种,它们都是冗赘无用的。

16) 推测的通用性(Speculative Generality)

是指为了“以防万一”,支持预期的未来功能,但这些功能并未被实现,这些类、方法、字段或参数也从未被使用,结果导致代码变得难以理解和支持。

17) 临时字段(Temporary Field)

创建临时字段以用于需要大量输入的算法。但这些字段仅在算法中使用,其余时间不使用。

18) 过长的消息链(Message Chains)

当客户端请求另一个对象,该对象又请求另一个对象,依此类推时,就会出现过长的消息链。这些链意味着客户端依赖于类结构的导航。一旦发生更改,客户端也要跟着修改。

19) 中间人(Middle Man)

指一个类只执行一个动作,但将工作委托给另一个类,这种委托属于过度委托。该类也可能只是一个空壳,只负责委托且只有一件事。

20) 内幕交易(Insider Trading)

指模块之间大量地交换数据,增加模块之间的耦合。

21) 异曲同工的类(Alternative Classes with Different Interface)

是指两个类执行了相同的功能但具有不同的方法名称。

22) 纯数据类(Data Class)

指包含字段和访问它们的粗略方法(getter 和 setter)的类。这些只是其他类使用的数据容器。这些类不包含任何附加功能,并且不能独立操作它们拥有的数据。

23) 被拒绝的遗赠(Refused Bequest)

指如果子类复用了超类的行为,但又不愿意支持超类的接口的情况。

24) 注释(Comments)

程序员将其作为一种“除臭剂”使用情况下的行为。比如:一段代码有着长长的注释,但这段长注释的存在是因为代码很糟糕。

四、 如何对代码“除臭”

1)重构

上述代码异味没有优先级一说,所以对于程序员而言,只能依靠直觉和经验去决定是否需要重构。 你代码的异味是故意的还是不小心?是故意的! 重构,一言以蔽之,就是在不改变外部行为的前提下,有条不紊地改善代码。是实现敏捷性的最重要的技术因素之一。是程序员根据已识别出的气味然后将代码分成更小的部分的过程,再决定要么删除它们,要么用更好的代码替换它们,如此循环重复这个过程,直到异味消失,这样可能会提高代码质量并让代码变得更具简单性、灵活性和可理解性。

2)使用代码检测工具

你代码的异味是故意的还是不小心?是故意的! 识别和消除代码异味是一个令人厌烦且不确定的过程,而且也不可能手动查找到和删除掉所有异味,尤其是面对一个有着上千行异味的代码的时候。所以使用一些代码检测工具可以辅助我们进行快速大量地审查,帮助我们节约时间来做更为重要的工作,比如能专注于代码高层面的设计原则问题。

好了,关于代码异味的知识,算是讲了个清楚,那么让我们相约下一次代码评审吧!

你代码的异味是故意的还是不小心?是故意的!

点赞
收藏
评论区
推荐文章
科工人 科工人
3年前
golang - DES加密ECB(模式)
Java默认DES算法使用DES/ECB/PKCS5Padding,而golang认为这种方式是不安全的,所以故意没有提供这种加密方式,那如果我们还是要用到怎么办?下面贴上golang版的DESECB加密解密代码(默认对密文做了base64处理)。
Stella981 Stella981
3年前
SpringBoot学习:整合shiro自动登录功能(rememberMe记住我功能)
首先在shiro配置类中注入rememberMe管理器!复制代码(https://oscimg.oschina.net/oscnet/675f5689159acfa2c39c91f4df40a00ce0f.gif)/cookie对象;rememberMeCookie()方法是设置Cookie的生成模
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Easter79 Easter79
3年前
SpringBoot学习:整合shiro自动登录功能(rememberMe记住我功能)
首先在shiro配置类中注入rememberMe管理器!复制代码(https://oscimg.oschina.net/oscnet/675f5689159acfa2c39c91f4df40a00ce0f.gif)/cookie对象;rememberMeCookie()方法是设置Cookie的生成模
Wesley13 Wesley13
3年前
C# 数据操作系列
0\.前言在上一篇中,我故意留下了查询的示范没讲。虽然说可以通过以下代码获取一个DataReader:IDataReaderreadercommand.ExecuteReader();然后通过reader一行一行的读取数据,但是我并不推荐这样使用。在查询这一高频需求上,C为之做了很多工作,提供了更多
Wesley13 Wesley13
3年前
6个关键要点解析故意伤害罪(附审查逮捕证据参考标准)
一、故意伤害罪的概念故意伤害罪(《中华人民共和国刑法》第二百三十四条),是指故意非法伤害他人身体并达成一定严重程度,应受刑法处罚的犯罪行为。二、故意伤害罪的犯罪构成(一)故意伤害罪的客体本罪侵犯的客体为他人的身体健康权。所谓身体健康权,是指自然人以保持其肢体、器官和其他组织的完整性为内容的人格权。(二)故意伤害
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
敏捷开发 敏捷开发
7个月前
你的代码是干的还是湿的?
DRY代码是一种软件原则,代表不要重复自己(Don’trepeatyourself),其目标是减少代码的重复。
敏捷开发 敏捷开发
7个月前
你的代码是干的还是湿的?
DRY代码是一种软件原则,代表不要重复自己(Don’trepeatyourself),其目标是减少代码的重复。
敏捷开发
敏捷开发
Lv1
女 · 产品经理
公众号:敏捷开发 网址:www.minjiekaifa.com
文章
44
粉丝
2
获赞
0