识别代码中的坏味道(三)

前两篇文章 《识别代码中的坏味道(一)》《识别代码中的坏味道(二)》 中已经介绍了 18 个代码坏味道。《重构》中还涉及到另外 4 个代码坏味道,本文将将详细介绍剩余的 4 个代码坏味道。

02.png

这四个代码坏味道是:

  1. 中间人(Middle Man)
  2. 狎昵关系
  3. 不完美的库类
  4. 被拒绝的遗赠

01 中间人(Middle Man)

在上一篇文章中 《识别代码中的坏味道(二)》 中在“过度耦合的消息链”这种代码坏味道曾经提及过中间人(Middle Man)这种代码坏味道,那么中间人到底是一类什么代码呢?

中间人指的是一种过度使用委托的代码,《重构》中给了一个参考值,如果一个类中有一半的方法都委托给其他对象进行,

为什么中间人是一种代码坏味道?

过度使用委托。这意味着当需求发生某些的变化的时候,这个中间人的类总是被牵连进来一并修改。这种中间人代码越多,浪费掉的时间也就越多。

如何解决中间人这种代码坏味道?

中间人的代码在于过度使用和委托两点。因此解决中间人这种代码坏味道就应该从减少委托下手:

删除中间人的方法,可以使用 Remove Middle Man(移除中间人)这种重构技巧。

当然如果原有代码的代理类中并不怎么变化,也可以选择延迟重构,依照“事不过三,三则重构”的原则可以选择当发生变化的时候进行重构。

02 狎昵关系(Inappropriate Intimacy)

指的是类之间花费太多的时间去探究彼此的私有的属性或者方法。

造成狎昵关系的原因可能是:

  1. 两个类本来就不应该拆分开;

  2. 两个类之间存在双向关联;

  3. 因为继承导致了狎昵关系;

    ...

为什么狎昵关系是一种代码坏味道?

  1. 狎昵关系会导致强耦合的表现;
  2. 而且类和类之间的职责将会变得模糊;
  3. 会因为访问对方的私有信息而导致过多的操作出现,或者产生封装上的妥协,让两个类纠缠不清。

如何解决狎妮关系这种代码坏味道?

  1. 通过 Move Field (搬移属性),Move Method(搬移方法)来移动属性和方法的位置,让属性和方法移动到它们本应该出现的位置。
  2. 如果直接移动属性和方法并不合适,可以尝试使用 Extract Class(提炼类)看是否能够找到公共类。
  3. 如果是因为相互调用导致的问题,可以尝试 Change Bidirectional Association to Unidirectional(将双向关联改为单向关联)尝试将关联关系划清。
  4. 如果是因为继承导致狎昵关系,可以尝试移除继承关系,改用代理类来实现。

03 不完美的库类

当直接使用第三方库的时候,导致代码可读性变差、意图不明确的问题。

为什么不完美的库类是一种代码坏味道?

第三方类库提供的功能能够在很场景下被复用。但是放在业务场景下,却总是要从业务视角切换到单纯的技术视角来来使用某些第三方类库。

例如

Date newStart = new Date(
                                    previousEnd.getYear(), 
                                    previousEnd.getMonth(), 
                                    previousEnd.getDate() + 1);

一眼看上去这是在表达什么意思其实并不容易看到。不完美的类库就在于造成代码中语意化变差。

如何解决不完美库类这种代码坏味道?

很多开发者会采用注释的方式期望让代码可读,但是这类注释本身也是一种代码的坏味道。不过可以借助函数名来揭示意图。

所以遇到上面例子的情况,可以使用 Extract Method 来提炼一个函数,生成如下代码

Date newStart = nextDay(previousEnd);


...


private Date nextDay(Date previousEnd) {
        return new Date(
                            previousEnd.getYear(), 
                            previousEnd.getMonth(), 
                            previousEnd.getDate() + 1); 
}

这样调用 nextDay() 的地方,就可以轻松的知道获取到 previousEnd 日期的下一天日期。

如果一个类中存在多种这种调用,或者多个类中都有类似的函数的时候,提炼一个单独一个类,并通过这个类对外提供这些方法无疑是一种消除重复提高复用的办法。实现这个类的方式可以使用代理的方式,也可以使用继承的方式。如果一个类只是提供代理方法,具体实现都要委托给类库,这样情况下,不如使用继承来生成的子类,并在子类中添加那些可以复用的方法。重构的过程可以参考 Introduce Local Extension(引入本地扩展)。

很显然第三方类库被设计的出发点往往是好的,但是实际调用的时候除了享受这种快速实现的方式,还需要关注第三方类库给当前项目带来的一些坏味道,并着手解决这些问题。

04 拒绝的遗赠

这个坏味道指的是当子类继承基类的时候,父类的一些方法即使子类并不需要也被迫被继承的情况。出现这种坏味道的一般有两种原因:

  1. 继承体系设计的不好,还需要调整;
  2. 基类实现了某个接口,导致子类不需要的时候也会实现那个接口对应的方法。

详细的例子可以参考:《重构分析21: 被拒绝的遗赠(Refused Bequest)》

为什么拒绝的遗赠是一种代码坏味道?

这个坏味道主要原因就是继承带来的坏味道,子类被迫实现某些方法或者从父类继承的方法对自身不但没有帮助甚至造成误导,比如:代码中通过继承实现 正方形 继承 长方形并求面积的例子,感兴趣可以参考《敏捷软件开发原则、模式、实践》中的里氏替换原则。

如何解决拒绝的遗赠的这种代码坏味道?

有两种思路:

  1. 改善继承体系。剔除子类不需要的方法,并创建子类的兄弟,通过 Move Method 将不需要的方法移动到兄弟类中,通过 Move Field 将涉及到非公共属性也移动到兄弟子类中。

  2. 使用代理来取代继承。这种方式的修改只涉及到对子类的调整,影响范围较小,并且也不会因此而像第一种重构方法那样因为要维护继承体系而导致一些新概念的产生。同时还能避免因为基类继承了某个接口,而导致的子类被迫实现某些方法的情况。

总结

至此 22 种常见的代码坏味道已经介绍完成。关注工具、框架的同时花一部分精力关注代码质量,能够让项目随着时间不断演进。当然实际工作中遇到的坏味道往往比这 22 种还要多。

项目中我们可以使用 Check StylePMDArch Unit 帮助我们及时发现项目中的问题,但是更”软“的部分需要我们花精力来理解清楚是什么、为什么、怎么解决。

或许你也已经发现了,很多情况下坏味道的原因在于变化时,无法快速应对变化,有的是代码设计的问题,有的是可读性的问题。即使代码坏味道也分为强烈的坏味道和淡淡的坏味道,所以重构的原则也是”事不过三、三则重构“,因此面对代码坏味道的时候如果代码坏味道很淡我们可以延迟消除坏味道。如果坏味道已经很强烈,或者淡淡的坏味道因为频繁的变化而导致效率下降时,那就不如先解决这种坏味道。

扩展

03.png

重构不是发生在项目结束的时候,而是融入在天天工作中进行的。采用TDD(测试驱动开发的方式)是一种很好的选择,熟悉TDD以及测试工具的情况下,TDD 不但不会降低速度反而让思路更加严谨、实现的代码实现质量更高。感兴趣的话可以参考:

《TDD 实战(1):体验》

《TDD 实战(2):Tasking To Action》

《TDD 实战(3):Simple Design》

05.png

参考

《重构》

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267