Code Smell

——如果尿布臭了,就换掉它。

《重构:改善既有代码的设计》,在该书的第3章“代码的坏味道”中,收录了Kent Beck关于重构时机的理解——Code Smell。

Code Smell的22种代码坏味道如下:

  1. Duplicated Code。a、同一个class内的两个函数含有相同的表达式。——需要Extract Method,提炼出重复代码,然后让两个地点都调用被提炼出来的那一段代码。b、两个互为兄弟的subclass内含相同的表达式,要避免这种情况——需要两个class都使用Extract Method,把Extract的Method推入superclass内。c、两个毫不相干的classes内出现Duplicate Code,你应该考虑对其中一个使用Extract Class,将重复代码提炼到一个独立class中,然后在另一个class内使用这个新class。——重复代码
  2. Long Method。应该积极地分解函数。a、每当感觉需要注释来说明点什么的时候,就把需要说明的东西封装成一个函数,并以用途(而非实现手法)命名。b、条件表达式和循环常常也是提炼的信号。——过长函数
  3. Large Class。应该重新抽象。大类就是你把太多的责任交给了一个类。——过大的类
  4. Long Parameter List。使用已有对象封装参数,或者制造一个参数对象。——过长参数列
  5. Divergent Change。一个类受到多种变化的影响。将对象拆分成多个对象,这么一来每个对象就可以只因一种变化而需要修改。——发散式变化
  6. Shotgun Surgery。这正好和上面相反。一种变化引发多个类相应修改。把需要修改的代码放进同一个类。——霰弹式修改
  7. Feature Envy。如果一个类的方法频繁用get 方法存取其他类的状态进行计算,那么你要考虑把行为移到涉及状态数目最多的那个类。有时候方法中只有一部分受这种依恋之苦,那就封装那部分把它带去梦中家园。有时候一个函数往往会用到几个类的功能,那就把这个函数放在拥有最多被此函数使用的数据的类中去。——依恋情节
  8. Data Clumps。将几个数据封装成对象。某些数据通常像孩子一样成群玩耍,一起出现在很多类的成员变量中,一起出现在许多方法的参数中,这些数据或许应该自己独立形成对象。一旦拥有新对象,就可以着手寻找Feature Envy。——数据泥团
  9. Primitive Obsession。将几个有关联的基本数据封装成对象。面向对象的新手通常习惯使用几个原始类型的数据来表示一个概念。譬如对于范围,他们会使用两个数字。对于Money,他们会用一个浮点数来表示。因为你没有使用对象来表达问题中存在的概念,这使得代码变的难以理解,解决问题的难度大大增加。好的习惯是扩充语言所能提供原始类型,用小对象来表示范围、金额、 转化率、邮政编码等等。——基本类型偏执
  10. Switch Statement。使用多态替换。少用switch语句,从本质上说,switch语句的问题在于重复。——switch惊悚现身
  11. Parallel Inheritance Hierarchies。可以让一个继承体系的实例引用另一个继承体系的实例。并行的继承层次是shotgun surgery 的特殊情况。每当你为某个类增加一个子类,必须也为另一个类相应增加一个子类。——平行继承体系
  12. Lazy Class。删除。某个类没有做足够的工作,请让这个类庄严赴义吧。——冗赘类
  13. Speculative Generality。删除。一个类实现了从未用到的功能和通用性。通常这样的类或方法唯一的用户是test case。不要犹豫,删除它。但如果它们的用途是帮助测试用例检测正当功能,当然必须刀下留人。——夸夸其谈未来性
  14. Temporary Field。抽象成类。一个对象的属性可能只在某些情况下才有意义。这样的代码将难以理解。专门建立一个对象来持有这样的孤儿属性,把只和他相关的行为移到该类。最常见的是一个特定的算法需要某些只有该算法才有用的变量。——令人迷惑的暂时字段
  15. Message Chain。提炼函数、隐藏分派。消息链发生于当一个客户向一个对象要求另一个对象,然后客户又向这另一对象要求另一个对象,再向这另一个对象要求另一个对象,如此如此。先观察消息链最终得到的对象是用来干什么的,看看能否以Extract Method把使用该对象的代码提炼到一个独立函数中,再运用Move Method把这个函数推入消息链。如果这条链上的某个对象有多位客户打算航行此航线的剩余部分,就加一个函数来做这件事。——过度耦合的消息链
  16. Middle Man。删除。对象的基本特性之一就是封装,而你经常会通过分派去实现封装。但是这一步不能走得太远,如果你发现一个类接口的一大半方法都在做分派,你可能需要移去这个中间人。——中间人
  17. Inappropriate Intimacy。划清界限,拆散,合并,单向联系。某些类相互之间太亲密,它们花费了太多的时间去专研别人的私有部分。对人类而言,我们也许不应该太假正经,但我们应当让自己的类严格遵守禁欲主义。——狎昵关系
  18. Alternative Classes with Different Interfaces。重命名,合并。做相同事情的方法有不同的函数签名,一致把它们往类层次上移,直至协议一致。——异曲同工的类
  19. Incomplete Library Class。使用装饰模式来包装类。要建立一个好的类库非常困难。我们大量的程序工作都基于类库实现。然而,如此广泛而又相异的目标对库构建者提出了苛刻的要求。库构建者也不是万能的。有时候我们会发现库类无法实现我们需要的功能。而直接对库类的修改有非常困难。这时候就需要用各种手段进行重构。——不完美的库类
  20. Data Class。将相关操作封装进去,减少public成员变量。对象包括状态和行为。如果一个类只有状态没有行为,那么肯定有什么地方出问题了。——纯稚的数据类
  21. Refused Bequest。用代理替代继承。超类传下来很多行为和状态,而子类只是用了其中的很小一部分。这通常意味着你的类层次有问题。——被拒绝的遗赠
  22. Comments。人们常把注释当做除臭剂来使用,经常觉得要写很多注释表示你的代码难以理解。当你感觉需要撰写注释时,请先尝试重构。——过多的注释

设计模式是你希望到达的目标,重构则是到达之路。

注1:“方法”、“行为”、“函数”、“功能”是一个意思。

注2:Switch Statement可参见这篇博文。https://blog.csdn.net/Agg_bin/article/details/92839080

发布了55 篇原创文章 · 获赞 61 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Agg_bin/article/details/92840307