代码的坏味道是指那些预示着需要重构的代码现象。以下 20 种常见坏味道逐一说明:

  1. Duplicated Code(重复代码):多处出现相同的代码逻辑,应提炼为公共代码。
  2. Long Method(函数过长):函数体积过大,应拆分为更小的函数。
  3. Large Class(过大的类):一个类承担了太多职责,应进行拆分。
  4. Long Parameter List(过长的参数列表):函数参数过多,可通过 ADT(抽象数据类型)来简化参数。
  5. Divergent Change(发散式变化):理想情况下,一旦需要修改,我们希望只跳到系统的某一点进行修改。如果一个类因为不同原因而频繁变化,说明存在此坏味道。
  6. Shotgun Surgery(散弹式修改):遇到某种变化时,必须在许多不同的类内做出许多小修改,要修改的代码遍布四处。
  7. Feature Envy(依恋情节):如果某个函数为了计算某个值,频繁调用另一个对象的取值函数,就该考虑将这个函数移到那个对象中去。此类问题还有很多例子。
  8. Data Clumps(数据泥团):两个类中存在系统性相同的字段、许多函数签名相同时,应考虑提炼出新的类,将数据的关注点转向函数。
  9. Primitive Obsession(基本类型的偏执):过度使用基本数据类型来表示概念,有时候使用 ADT 更为合适,但也要避免过度抽象。
  10. Switch Statements(Switch 语句):面向对象语言的明显特征之一,就是尽量用多态来替代 switch/case 语句。
  11. Parallel Inheritance Hierarchies(平行继承体系):每当为某个类增加子类时,必须也为另一个类增加子类,两个继承体系互相平行地增长。
  12. Lazy Class(冗余类):如果一个类没有足够的价值,应该考虑去掉它。
  13. Speculative Generality(夸夸其谈未来性):如果试图用各种手法来处理非必要的情况("以后可能用到"),结果往往造成系统更难维护和理解,需要慎重考虑。
  14. Temporary Field(暂时字段):类中某个变量只在特殊情况下才会被用到,这样的代码不易理解。通常认为类中的变量应该被所有对象使用,临时字段应该提炼出来。
  15. Message Chains(过度耦合的消息链):当看到一个对象请求另一个对象,然后又请求另一个对象,就可能出现 getThis().getThat().getOther() 这样一长串的调用链。这意味着结构紧密耦合,一旦对象间关系发生变化,客户端就不得不跟着变化。解决办法是先观察消息链最终是为了什么,再看能否提炼出独立的函数来封装这个意图。
  16. Middle Man(中间人的过度使用):如果某个类的接口中有一半函数都在委托给其他类,那么就应该考虑移除这个中间人类,或者让它变成子对象。
  17. Inappropriate Intimacy(不恰当的亲密关系):如果两个类的关系过于紧密、互相依赖,需要慎重考虑它们之间的关系是否合理。
  18. Alternative Class With Different Interfaces(异曲同工的类):如果两个类或函数的行为几乎相同,可以考虑提炼合并,去掉冗余代码。
  19. Data Class(纯粹的数据类):只有字段和访问器的类,应该考虑让它承担更多行为职责。
  20. Refused Bequest(被拒绝的遗赠):在继承体系中,如果子类只具备父类的部分特性或函数,意味着这个继承体系设计是错误的,尤其需要注意抽象超类的使用。