|||
软件工程大师Martin Fowler和Kent Beck用“代码味道(Code Smells)”一词来描述这些不良实现方案,他定义了22种常见的代码味道,包括数据泥团、重复代码、霰弹式修改等,检测、去除代码味道对于提高代码质量具有非常重要的意义。在一定程度上,去除代码味道能够改善代码的可维护性,对程序代码中的代码味道进行检测和合理地重构,在不改变程序外部行为的前提下改善代码的质量。一般而言,开发人员往往在对代码味道进行重构之前,人工地审查代码、检测代码味道,这样的方式效率低下,且正确率不高。近年来,自动检测代码味道已成为软件工程领域的一个研究热点。例如,泛化重构机会检测进行了研究,并基于概念关系和相似性等开发了一款重构时机检测工具;从软件设计模型的角度检测代码味道,将机器学习的算法运用于代码味道数据集,并根据度量指标检测不同类型的代码味道,并在代码优化方面的性能进行评估。针对检测代码味道这一问题也诞生了一些自动化工具,例如,PMD可以检查源代码中存在的过大类、重复代码等代码味道,允许用户自定义检测这些代码味道的阈值;CheckStyle能够探测代码大小是否违规、类的设计是否良好,也能找出过长函数和过长参数列等代码味道。inFusion能够探测超过20种代码味道和代码缺陷,探测到的代码味道包括重复代码,破坏封装的类(如数据类或者万能类),高耦合的方法和类,以及一些设计不合理的类继承结构。
状况:如果你在一个以上的地点看到相同的程序结构,那么当可肯定:设法将它们合而为一,程序会变得更好。
1、同一个class内的两个函数含有相同的表达式。——需要Extract Method,提炼出重复代码,然后让两个地点都调用被提炼出来的那一段代码。
2、两个互为兄弟的subclass内含相同的表达式,要避免这种情况——需要两个class都使用Extract Method,把Extract的Method推入superclass内。
3、两个毫不相干的classes内出现Duplicate Code,你应该考虑对其中一个使用Extract Class,将重复代码提炼到一个独立class中,然后在另一个class内使用这个新class。
不熟悉面向对象技术的人,常常觉得对象程序中只有无穷无尽的delegation(委托),根本没有进行任何计算。和此类程序共同生活数十年之后,你才会知道,这些小函数有多大价值。——间接层所能带来的全部利益:解释能力,共享能力,选择能力。——都是由小型函数支持的。
很久以前程序员就已认识到:程序愈长愈难理解。不过短函数让代码阅读者必须经常转换上下文去看看子程序做了什么,然短函数容易理解的真正关键在于一个好名字。如果你能给函数起个好名字,读者就可以通过名字了解函数的作用,根本不必去看其中写了些什么。
可遵循的一条原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途命名。——关键在函数“做什么”和“如何做“之间的语义距离。
函数内如果有大量的参数和临时变量,会对你的函数提炼形成阻碍,导致可读性几乎没有任何提升。——可以使用Replace Temp with Query来消除暂时元素。Introduce Parameter Object则可以将过长的参数列变的更简洁些。
如果仍然有太多临时变量和参数,那就应该“用对象替换方法”。
如何确定该提炼哪一段代码?寻找注释。寻找条件式和循环。
引自:http://www.cnblogs.com/baochuan/archive/2012/05/06/2443365.html
21种代码的“坏味道”
1.Duplicated Code
代码重复几乎是最常见的异味了。他也是Refactoring 的主要目标之一。代码重复往往来自于copy-and-paste 的编程风格。与他相对应OAOO 是一个好系统的重要标志(请参见我的duplicated code 一文:http://www.erptao.org/download.php?op=viewsdownload&sid=6)。
2.Long method
它是传统结构化的“遗毒“。一个方法应当具有自我独立的意图,不要把几个意图放在一起,我的《大类和长方法》一文中有具体描述。
3.Large Class
大类就是你把太多的责任交给了一个类。这里的规则是One Class One Responsibility。
4.Divergent Change
一个类里面的内容变化率不同。某些状态一个小时变一次,某些则几个月一年才变一次;某些状态因为这方面的原因发生变化,而另一些则因为其他方面的原因变一次。面向对象的抽象就是把相对不变的和相对变化相隔离。把问题变化的一方面和另一方面相隔离。这使得这些相对不变的可以重用。问题变化的每个方面都可以单独重用。这种相异变化的共存使得重用非常困难。
5.Shotgun Surgery
这正好和上面相反。对系统一个地方的改变涉及到其他许多地方的相关改变。这些变化率和变化内容相似的状态和行为通常应当放在同一个类中。
6.Feature Envy
对象的目的就是封装状态以及与这些状态紧密相关的行为。假如一个类的方法频繁用get 方法存取其他类的状态进行计算,那么你要考虑把行为移到涉及状态数目最多的那个类。
7.Data Clumps
某些数据通常像孩子一样成群玩耍:一起出现在很多类的成员变量中,一起出现在许多方法的参数中…..,这些数据或许应该自己独立形成对象。
8.PRimitive Obsession
面向对象的新手通常习惯使用几个原始类型的数据来表示一个概念。譬如对于范围,他们会使用两个数字。对于Money,他们会用一个浮点数来表示。因为你没有使用对象来表达问题中存在的概念,这使得代码变的难以理解,解决问题的难度大大增加。 好的习惯是扩充语言所能提供原始类型,用小对象来表示范围、金额、转化率、邮政编码等等。
9.Switch Statement
基于常量的开关语句是OO 的大敌,你应当把他变为子类、state 或strategy。
10. Parallel Inheritance Hierarchies
并行的继续层次是shotgun surgery 的非凡情况。因为当你改变一个层次中的某一个类时,你必须同时改变另外一个层次的并行子类。
11. Lazy Class
一个干活不多的类。类的维护需要额外的开销,假如一个类承担了太少的责任,应当消除它。
12. Speculative Generality
一个类实现了从未用到的功能和通用性。通常这样的类或方法唯一的用户是testcase。不要犹豫,删除它。
13. Temporary Field
一个对象的属性可能只在某些情况下才有意义。这样的代码将难以理解。专门建立一个对象来持有这样的孤儿属性,把只和他相关的行为移到该类。最常见的是一个特定的算法需要某些只有该算法才有用的变量。
14. Message Chain
消息链发生于当一个客户向一个对象要求另一个对象,然后客户又向这另一对象要求另一个对象,再向这另一个对象要求另一个对象,如此如此。这时,你需要隐藏分派。
15. Middle Man
对象的基本特性之一就是封装,而你经常会通过分派去实现封装。但是这一步不能走得太远,假如你发现一个类接口的一大半方法都在做分派,你可能需要移去这个中间 人。
16. Inappropriate Intimacy
某些类相互之间太亲密,它们花费了太多的时间去砖研别人的私有部分。对人类而言,我们也许不应该太假正经,但我们应当让自己的类严格遵守禁欲主义。
17. Alternative Classes with Different Interfaces
做相同事情的方法有不同的函数signature,一致把它们往类层次上移,直至协议一致。
18. Incomplete Library Class
要建立一个好的类库非常困难。我们大量的程序工作都基于类库实现。然而,如此广泛而又相异的目标对库构建者提出了苛刻的要求。库构建者也不是万能的。有时候我们会发现库类无法实现我们需要的功能。而直接对库类的修改有非常困难。这时候就需要用各种手段进行Refactoring。
19. Data Class
对象包括状态和行为。假如一个类只有状态没有行为,那么肯定有什么地方出问题了。
20. Refused Bequest。
超类传下来很多行为和状态,而子类只是用了其中的很小一部分。这通常意味着你的类层次有问题。
21. Comments。
经常觉得要写很多注释表示你的代码难以理解。如果这种感觉太多,表示你需要Refactoring。
在计算机编程社区中,code smell代表了任何标志着事物变坏的征兆。它常常标志代码应该被refactored或者全部的设计都应该被reviewed。这个短语出现在 WardsWiki上,它是被Kent Beck杜撰出来的。在refactoring兴起之后,这个短语的使用率骤增。
判断是否存在code smell经常是主观判断,并且随着语言、开发者、开发理论的不同而存在差异。
经验丰富和知识渊博的开发人员通过对优秀设计有一种“感觉”,他们已经达到一种称之为“无意识能力 (UnconsciousCompetence)”的状态。也就是说,他们无需思考,只要通过查看代码或一段设计就可以立马对这个项目的代码质量有一种 “感觉”,能够对代码设计的优劣有一个大致的判断。
但大家需要注意,code smell只是一种“暗示”,而非一种“确定”。将某些事物称之为“code smell”并未是一种攻击,它只是一种提示:开发人员需要对项目设计进行更进一步的查看。因此,code smell更多是“直觉的,本能的”。
引自:http://www.knowsky.com/362766.html
代码味道识别工具 (Code Smell Detection Tools, CSDT)既可以应用于软件开发阶段,也可以应用于软件维护阶段。目前大部分IDE都集成了自动或者半自动重构工具,但是根据国外研究人员的调查发现,真正在软件开发和维护中使用重构工具的人并不多,其中有一个很重要的原因是不知道何时该重构,也就是说找不准重构时机,因此重构时机的自动识别具有很大的实用价值。很多代码味道本身就是重构时机的指示器,如果能够自动发现这些代码味道,就说明代码中存在一些不恰当的设计方案或者程序代码,在此时再实施重构将有利于提高代码质量。
当前大部分CSDT在发现代码味道之后并不能实施自动重构(在接下来要介绍的七款工具中,只有JDeodorant提供了重构功能),还好现代IDE都自带了重构功能,尽管很多重构还是需要用户参与,只是半自动重构,但是这已经比人工重构好很多了。人工重构主要存在两个问题:
(1) 重构效率低。特别是对于一些较为复杂的重构,如果全部按照Martin Fowler书中的示例一步步操作,效率很低,使得重构这一非创造性工作耗费大量的时间和精力,划不来,不可取。
(2) 人工重构容易犯错。只要程序本身正确,并能够通过严格的正确性和可靠性测试,自动重构的结果往往比人工重构的结果很准确,而人工重构的质量需要取决于重构人员本身的经验,一不小心就会存在一些错误的重构或者重构不完整。
本文将对七款CSDT进行简要的介绍(如果有时间的话在后续博客中再对这些工具分别进行详细的说明),并提供了下载链接,没有用过的童鞋可以试着用用,使用过的童鞋也欢迎与Sunny交流使用心得,特别是对这些工具有什么意见和建议,这对Sunny当前的研究工作很有帮助,。
【注:如果不明白这些代码味道的具体含义,请参阅:22种代码味道(Martin Fowler与Kent Beck),URL:http://blog.csdn.net/lovelion/article/details/9301691。】
(1) Checkstyle
URL: http://checkstyle.sourceforge.net/
Checkstyle是一款知名度很高的静态代码分析工具,用于帮助程序员按照一些代码标准来编写Java代码,它内置了许多规则,可以验证Java代码是否符合这些预定义的规则。Checkstyle可以检测Large Class,Long Method,Long Parameter List和Duplicated Code四种代码味道。
(2) DECOR
URL: http://www.ptidej.net/research/designsmells/
DECOR由Moha等人设计和开发,它定义了一组方法用于自动检测设计和代码味道(又称为反模式Anti-Pattern)。他们使用定制的语言描述了八种代码味道(分别是Data Class,Large Class,Long Method,Long Parameter List,Message Chains,Refused Bequest,Speculative Generality,Tradition Breaker),并使用模板自动生成它们的探测算法,最后依据精确率(Precision)和召回率(Recall)来验证这些算法,在DECOR中实现了这些用于软件分析的算法。DECOR既是Moha等提出的方法的名字,也是他们所开发的代码味道检测工具的名字。
【注:获取DECOR的安装程序需要向作者(PtidejTeam)发电子邮件,邮箱地址:decor@ptidej.net】
(3) iPlasma
URL: http://loose.upt.ro/iplasma/
iPlasma是一个用于评估面向对象系统质量的集成平台,它涉及模型提取、高层度量分析等多个方面。iPlasma用于检测一些作者预先定义的代码问题,这些问题主要分为三类:身份问题(Identity Disharmonies)、协作问题 (Collaboration Disharmonies)和分类问题(Classification Disharmonies),关于这些问题的详细介绍可以参考Object-Oriented Metrics in Practice: Using Software Metrics to Characterize, Evaluate, and Improve the Design of Object-Oriented Systems一书,这些问题中包括不少代码味道,例如Duplicated Code(在iPlasma中叫做Significant Duplication),God Class,Feature Envy和Refused Bequest等。当然,iPlasma并没有实现Object-Oriented Metrics in Practice中所有问题的探测,在Object-Oriented Metrics in Practice一书中,身份问题(Identity Disharmonies)包括God Class,Feature Envy,Data Class,Brain Method,Brain Class和Significant Duplication,协作问题 (Collaboration Disharmonies)包括Intensive Coupling,Dispersed Coupling和Shotgun Surgery,分类问题(Classification Disharmonies)包括Refused Parent Bequest和Tradition Breaker。对这些味道及其详细说明感兴趣的童鞋可以看看Refactoring: Improving the Design of Existing Code和Object-Oriented Metrics in Practice: Using Software Metrics to Characterize, Evaluate, and Improve the Design of Object-Oriented Systems这两本书。
(4) inFusion
URL: http://www.intooitus.com/inFusion.html
inFusion是iPlasma的商业版本,因此inFusion的功能比iPlasma更加强大,不过因为它是一款商业软件,所以很多功能需要购买正式版才能使用,而且价格还不便宜,下图是最新的价格表,价格单位是欧元哦,欧元!inFusion能够探测超过20中代码味道和代码缺陷,在本文所介绍的七种工具中是最多的,不过也就它要钱,它能够探测到的代码味道包括重复代码,破坏封装的类(如数据类或者万能类),高耦合的方法和类,以及一些设计不合理的类继承结构。
(5) JDeodorant
URL: http://www.jdeodorant.com/
从学术的角度来说,JDeodorant是一款很强大的工具,催生了多篇高水平研究论文的诞生。它是一个Eclipse插件,可以用于自动识别Java程序中存在的Feature Envy,God Class,Long Method和Switch Statement(在JDeodorant中称为Type Checking)等代码味道。针对识别到的代码问题,JDeodorant将向用户提供多种合适的重构方案,并按照它们对设计的影响进行排序,然后将排序结果呈现给开发人员以供开发人员选择,最后还能够自动实施所选择的重构手段。
(6) PMD
URL: http://pmd.sourceforge.net/和https://github.com/pmd
PMD在代码静态分析领域知名度非常高,它可以扫描Java源代码并寻找代码中潜在的问题或者可能存在的bug,例如dead code(死代码),empty try/catch/finally/switch statement(空的语句块),unusedlocal variables or parameters(没有使用的局部变量或参数)和duplicated code(重复代码)等。PMD也可以探测一些代码味道,例如Large Class,Long Method,Long Parameter List和Duplicated Code等,它允许用户在探测这些代码味道时设置一些探测度量因子的阈值,例如Large Class和Long Method的代码行数,Long Parameter List的方法参数个数,Duplicated Code中重复代码的数量等,这些都可以由用户自己来设定。
(7) Stench Blossom
URL: http://people.engr.ncsu.edu/ermurph3/tools.html
Stench Blossom是一个提供了交互式可视化环境的味道识别工具,它可以向程序员快速提供一个代码味道的高层视图以及味道的来源。Stench Blossom也是一个Eclipse插件,它向程序员提供了三个不同的视图,通过这三个视图逐步以可视化方式提供更多关于这些代码味道的信息。它最终以可视化方式返回一个综合的反馈结果,在IDE中以花瓣图的方式来表示代码单元,花瓣的大小与某一代码单元中存在的味道“强度”成正比,也就是说存在的代码味道越多,对应的花瓣越大。寻找这些代码味道的唯一可能方法就是人工审查源代码,主要是分析那些存在代码味道的大花瓣。Stench Blossom可以探测到8种代码味道,分别是Data Clumps,Feature Envy,Instanceof,Large Class,Long Method,Message Chains,Switch Statement和Typecast。这些味道有些并不属于Martin Flower所定义的22种经典代码味道。
表1对这七个工具的一些基本特征进行了总结,不难看出:所有的工具都可以对Java代码进行味道识别,部分工具可以识别C/C++中的代码味道;有一些工具作为单机版(Standalone)发布,也有一些工具作为Eclipse插件(Plugin)发布;只有JDeodorant提供了自动重构(Refactoring)功能;部分工具只能检测到代码中存在的味道,但是没有对所识别到的味道进行定位。具体情况如表1所示:
表2对这七个工具所能够探测到的代码味道进行了统计,作为商业工具的inFusion所能够识别到的代码味道最多(12种),其次是iPlasma(11种)。
表2中一共包括21种代码味道,但并非所有味道都是Martin Fowler在《重构》一书中定义的那22种代码味道。本文所介绍的七种工具最多也就能够识别一半味道多一点,不知道新版本是否可以识别更多的味道。如果能够识别到更多的味道(除了这21种外,还有一些其他的代码味道),而且识别速度还过得去,我想应该可以开发出更强大的代码味道识别工具。
由于不同的代码味道识别工具的实现原理不同,因此,对于同一种代码味道,不同的工具的识别结果并不一定相同,有国外学者对这些工具的准确率进行了比较,不同的工具对于某种(些)味道可能具有一些优势,但对于其他味道可能效果并不是最好。缺乏统一的标准和规范,这也是当前代码味道识别所面临的一个问题,国内从事相关研究和工具开发的人较少,很多企业对于代码质量的重视程度不够,导致代码味道识别工具的推广和使用并不太给力,希望在将来会有所好转。
除上述七款工具之外,还有一些用于探测代码味道的工具和方法。例如CodeVizard (基于Object-Oriented Metrics in Practice一书所定义的探测规则),inCode(基于inFusion的Eclipse插件),ExCop (for .NET),Analyst (for Java,商业软件),JCosmo (for Linux),CloneDigger和ConQat(用于克隆检测),其中,克隆检测工具应该是最多的,Sunny计划整理一篇专门的博文来介绍常用的克隆检测工具。
本文中所介绍的这些工具大家都可以试着用用,结合Eclipse中的Refactoring菜单一起使用应该有利于提高代码质量。如有代码味道识别和重构方面的问题,欢迎与Sunny交流讨论!
参考文献:
[1] F. A. Fontana, P. Braione, M. Zanoni. Automatic detection of bad smells in code: An experimental assessment [J].Journal of Object Technology, 2012, 11(2): 1-38.
[2] F. A. Fontana, E. Mariani, A.Morniroli, et al. An Experience Report on Using Code Smells Detection Tools[C]. 2011 IEEE Fourth International Conference on Software Testing, Verification and Validation Workshops (ICSTW), Berlin, Germany, 2011: 450-457.
[3] N. Moha, Y. Guéhéneuc, L. Duchien, A.Le Meur. DECOR: A Method for the Specification and Detection of Code and Design Smells[J]. IEEE Transactions on Software Engineering,2010, 36(1): 20-36.
[4] A. Hamid, M. Ilyas, M. Hummayun, A.Nawaz. A Comparative Study on Code Smell Detection Tools [J]. International Journal of Advanced Science and Technology, 2013, 60: 25-32.
[5] N. Tsantalis, T. Chaikalis, A. Chatzigeorgiou. JDeodorant: Identification and Removal of Type-Checking Bad Smells [C]. 12th European Conference on Software Maintenance and Reengineering, 2008 (CSMR 2008), Athens, Greece, 2008: 329-331.
[6] M. Fokaefs, N. Tsantalis, A.Chatzigeorgiou. JDeodorant: Identification and Removal of Feature Envy Bad Smells [C]. 2007 IEEE International Conference on Software Maintenance (ICSM2007), Paris, France, 2007: 519-520.
[7] M. Fokaefs, N. Tsantalis, E. Stroulia, A. Chatzigeorgiou. JDeodorant: identification and application of extract class refactorings [C]. 2011 33rd International Conference on Software Engineering(ICSE), Honolulu, HI, USA, 2011: 1037-1039.
【作者:刘伟 http://blog.csdn.net/lovelion】
Archiver|手机版|科学网 ( 京ICP备07017567号-12 )
GMT+8, 2024-5-29 17:09
Powered by ScienceNet.cn
Copyright © 2007- 中国科学报社