阅读 180

来来竖三我跟你说说代码的坏味道

重构三部曲:

  1. 刀哥我打算重构这块代码
  2. 来来竖三我跟你说说代码的坏味道
  3. 刀哥咱俩探讨一下重构的手法

周六的清晨,光线照进了阴暗的办公室里,原本黑暗的室内瞬间变得透亮,定睛一看还能瞧见灰尘在光线中起舞。刀哥伸着懒腰,打着哈欠,项目终于在通宵中告一段落。刀哥不经意间瞥见了竖三,突然想起了上次跟他说的重构只说了一半,由于赶着项目上线,一直没时间跟他说另一半,正好今天项目完结了,我跟他讲讲。

刀哥站起身来,有气无力的走到竖三身边说:"三,上次欠你的重构的债还没还呢,今天我跟你说说代码的坏味道吧?"

竖三转头看着刀哥,两只眼睛似睁未睁,无力地说:"刀哥你饶了我吧,一宿没睡,我现在眼睛都睁不开了,我要回家躺尸了,改天吧。"

"我也回去,正好咱俩顺路,走路上给你讲讲,改天不知道改成那天了。"

"可以"

说吧,两人起身走着。

代码坏味道说的是这块代码有点腐烂,有些可能腐烂一丁点,不影响整体的使用。有些可能腐烂一大片,此时代码可读性差,扩展不易,维护成本高,应该及时清理腐烂部分,避免破窗效应的发生。

常见的坏味道有:

  • 重复代码(Duplicated Code):这个可是坏味道的大腕,有多处重复代码,修改了一处,另一处没修改,就出错,见到它赶紧收拾了。

  • 过长函数(Long Method):一个函数几十行,看也看不懂,调试才知道干啥子的,见着了就头大,为了大家愉快的工作,见了就修了。

  • 过大的类(Large Class):一个类应该一个妻子就行了呗,非要整个三妻四妾,违反了单一职责。

  • 过长参数列(Long Params List):一般三个参数就已经够多了,超过三个就噩梦了,不知参数所云,见了盘盘它。

  • 发散式变化(Divergent Change):一个类受到多个变化的影响,稍稍动一下,类就要修改,违反了单一职责。

  • 霰弹式修改(Shotgun Surgery):修改了一处,多个类需要修改,麻烦呀。

  • 依恋情结(Feature Envy):有着自己的对象,不好好干着自己的事,还关心别人的对象接口,关心就关心呗,职责却是调用别人对象大部分函数来完成,这就不满足GRASP(General Responsibility Assignment Software Patterns)的信息专家了。信息专家说的是把职责分配给具有完成职责所需信息的类。

  • 数据泥团(Data Clumps):说的也是不检点的事,自己融合了太多数据,拆出来多好。

  • 基本类型偏执(Primitive Obsession): 有些可以使用类表述更清楚的,却使用基本类型,比如电话号码。

  • Switch惊悚现身(Switch Statements):Switch一出现基本是根据类型做相应的处理,一旦类型加了,减了就要大片修改,把类型选择的处理抽象出类,可以避免此问题,比如抽出成策略模式,命令模式等等。

  • 平行继承关系(Parallel Inheritance Hierarchies):一个类继承体系的子类增加,导致另一个继承体系类也要加子类。说明类不是依赖抽象,而是依赖实现。想办法搞掉。

  • 冗赘类(Lazy Class):类的维护成本高于它的价值,那妥妥的需要让它英勇赴义。一般是前期规划应对某种变化,但是没有出现这种变化。

  • 夸夸其谈未来性(Speculative Generality):以后我这代码可以应对java接口变化,我擦,Java接口是稳定的,你应对这种变化干嘛。此情况往往是识别的变化有问题,以为后面有变化,稳稳地设计了应对变化的代码,到头来不会有此种变化,这段代码也就废了。及时处理,省得操心。

  • 令人迷惑的暂时字段(Temporary Field):类中的某个变量只为特定情况而设,这就很尴尬,看一眼完全没感觉,抽出去让它适得其所,大家都皆大欢喜。

  • 过长的信息链(Message Chains):XXobject.XX.XXX.XXX,一般以这种格式出现,就说明信息链过长,这就违法了迪米特法则(最少知识原则),不要和陌生人说话。这种情况不光是和陌生人说话,还和陌生人喝起酒来了。Builder模式也是这种格式调用,但是没有此问题,因为返回的是同一个对象,没有和陌生人说话。

  • 中间人(Middle Man):对象的特征之一就是封装,而封装往往伴随着委托。但万事有利就有弊,过度委托就适得其反,一半以上函数都是委托,那移除委托还是比较好,省得还要交中介费。

  • 狎昵关系(Inappropriate Intimacy):这种情况好比是红杏出墙,类之间相互试探彼此的private成分,及时制止,让它们严守清规。此情况违反了GRASP(General Responsibility Assignment Software Patterns)的信息专家,动动手让方法或者字段挪挪窝。

  • 异曲同工的类(Alternative Classes with Different Interfaces):两个类中的两个函数却做了同一个事,修了免得以后出错。

  • 不完美的库类(Incomplete Library Class):对于引入的arr或者jar包,总想让某个类实现某个功能,可是呢类里面没有,你也没办法修改库里面的类。比如说我们想让java中的String实现一个获取最后一个字符的方法,可是呢没有,引入函数的方式并以String作为参数。非final类也可以继承来增加功能。

  • 幼稚的数据类(Data Class):这样的类只包含读写函数,除此之外一无是处。

  • 被拒绝的遗赠(Refused Bequest):子类应该继承父类的数据以及函数,但是子类不想继承父类的部分方法或者函数就出来了此问题,这说明父类的一些接口不是所有的子类都需要,可以新建兄弟类,把不需要的函数和数据移动到兄弟类中,保证正确的继承关系。

  • 过多的注释(Comments):注释更容易让代码理解,但给可读性差,无法自释的代码写注释就有问题,见到这类问题及早修改。

“这就是所有的代码的坏味道了” 刀哥说着。

“基本听明白了,那见到这些坏味道又该怎么处理呢?” 竖三说。

“知道什么是坏代码,并不能写出好代码,知道坏味道的代码,并不一定知道如何去除坏味道。至于如何写出好代码,如何去除代码的坏味道,我下次再给你说吧。车都快到站了。”

“OK,那周一见了再聊吧。”

“OK”

参考阅读:

  • 《重构:改善既有代码的设计》
  • 《代码整洁之道》

限于个人水平,有错误请指出,大家共同学习进步!

扫码关注公众号,查看更多内容。