遗留系统现代化实战
姚琪琳
Thoughtworks 资深咨询师
5615 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 30 讲
用户故事 (1讲)
遗留系统现代化实战
15
15
1.0x
00:00/00:00
登录|注册

09 | 代码现代化:如何将一个300行的方法重构为3行?

你好,我是姚琪琳。
在上节课里,我们学习了如何对遗留代码做可测试化重构,并添加测试。有了测试的保障,接下来就可以大胆地开始重构“烂代码”了。
在重构了大量遗留代码后,我终于找到了两个最实用的方法,这节课我就带你认识这两种重构遗留代码的利器,我把它们称为“倚天剑”和“屠龙刀”,可以帮你劈开一团乱麻式的代码。
我曾经用这两种模式将一个 300 行代码重构为 3 行。是不是感觉很神奇?

基于坏味道的重构

在此之前,我先来简单絮叨两句我们重构代码的原则,就是基于坏味道来重构。也就是说,我们在重构时,要尽量先去识别《重构》中总结的二十几种坏味道,再用书中对应的重构手法去重构。
你可能会质疑,要不要这么教条啊?这其实并不是教条。Martin Fowler 已经“阅码无数”,甚至可能比我吃的饭都多。他总结出来的坏味道已经足够典型,对应的重构手法也足够好用。我也承认我的智商远不如他,那为什么不能拿来主义呢?
和第六节课学习代码增量演进时一样,在重构代码之前,我还是先带你识别坏味道,然后再重构。遗留系统的代码,简直是最具代表性的“代码坏味道大观园”。
尽管重构起来挑战重重,但攻克它们又令人上瘾、着迷、欲罢不能。我这样安排,是为了授之以渔(即重构的方法),而不光是授之以鱼(即重构好的方法)。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

重构遗留代码的两种实用方法——倚天剑和屠龙刀,为开发者提供了重构代码的实际操作指南。倚天剑通过拆分阶段将方法中的多个职责分离,清晰地组织代码逻辑,提高可读性和可维护性。而屠龙刀则能够彻底斩断职责不相关的代码关系,通过将方法移动到不同的方法对象中,实现了代码的彻底分离。作者还提到了重构到策略模式的思路,通过提取接口,让不同的calculator实现同一个接口,达到了重构到设计模式的效果。文章详实介绍了重构方法和技术特点,适合开发者快速了解并获得对重构方法的深入了解。文章还提到了重构到领域模型的思路,通过将计算的逻辑都放到了对象中,达到了领域模型模式。总结而言,本文为读者提供了实用的重构方法和技术特点,帮助开发者更好地理解和应用重构技术。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《遗留系统现代化实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(8)

  • 最新
  • 精选
  • aoe
    原来《重构》第二版中介绍了这么多代码的坏味道,还没看过,立马买了一本。 目前掌握的发现坏味道的知识大部分来自郑烨老师的专栏《软件设计之美》、《代码整洁之道》、《修改代码的艺术》 现在又多了一个来源:跟着姚琪琳老师学习发现代码的坏味道 如果看过专栏《徐昊 · TDD项目实战70讲》的1~4课后,再看本篇文章会有一种风生水起的感觉! 之前一直看徐昊老师 inline,不懂其奥义,今天才明白:原来在重构的时候经过 inline 的代码可以摆脱调用方法的限制,想移动到哪都可以

    作者回复: 加油💪

    2022-05-04
    2
    8
  • 云岭牧少歌
    老师,我有一个疑问,最终重构后Performance类看起来也不是单一的吧,calculateAmount和calculateCredits各有一个修改的原因,但感觉amount和credits的计算又应该放到Performance类里,很矛盾,那怎么理解呢? public class Performance { private int audience; private Play play; public int calculateAmount() { int thisAmount = 40000; if (audience > 30) { thisAmount += 1000 * (audience - 30); } return thisAmount; } public int calculateCredits() { int thisCredits = Math.max(audience - 30, 0); if ("comedy".equals(play.type)) thisCredits += Math.floor((double) audience / 5); return thisCredits; } }

    作者回复: 好问题! 你可以理解为,Performance提供的这两个能力,都是同一个抽象级别的。Bob大叔在Clean Architecture里说的,单一职责的单一指的是单个抽象级别。 但为什么这两个方法在TheatricalPlayers类里就不是同一个抽象级别呢?因为这个类主要使用来打印,这些计算跟打印就属于不同的抽象级别了。

    2022-05-08
    5
  • zcc
    确实挺过瘾的,我不知道自己看了多少遍了,每次看完都身心愉悦,太值了!哪怕吵架了心情不好了,也可以看,O(∩_∩)O哈哈~ public String print(Invoice invoice) { int totalAmount = invoice.calculateTotalAmount(); int volumeCredits = invoice.calculateVolumeCredits(); return resultFormatter.getResult(invoice, totalAmount, volumeCredits); } 到这一步,ResultFormatter对象的getResult方法对Invoice对象也有依恋情结,同样按下F6就移动Invoice了。看起来就更清爽,三个步骤完成: public String print(Invoice invoice) { int totalAmount = invoice.calculateTotalAmount(); int volumeCredits = invoice.calculateVolumeCredits(); return invoice.format(totalAmount, volumeCredits); }

    作者回复: 没错,ResultFormatter也只依赖了Invoice,但我这里并没有把格式化的逻辑也放到Invoice里,这是因为格式化结果本身不应该是Invoice领域模型的一部分(当然,关于某个逻辑是否属于某个领域模型,需要团队讨论达成共识),Invoice所包含的应该是各种金额数据、计算总额以及优惠,格式化属于一个横切的需求。而且,当格式化的逻辑也会发生变化的时候,放到Invoice里面就会更加僵化。

    2023-08-24归属地:上海
    2
    1
  • zahi
    老师能不能也提提非面向对象的实现方式。
    2022-05-21
    1
  • zcc
    老师从拆分阶段,到引入方法对象,然后到消消乐消除了三个方法对象(方法都到Performance和Invoice对象上了),给我们展示了整个重构过程。就本例代码而言,拆分阶段完成后,就可以按F6用IDEA的Move Instance Method重构手法,将仍发散在TheatricalPlayers类里的私有方法分别移动到Performance和Invoice对象上。
    2023-08-24归属地:上海
  • Spoon
    方法对象&重构发到领域模型,受益匪浅~
    2023-03-12归属地:浙江
  • fliyu
    让我明天再看看一遍
    2023-02-06归属地:广东
  • 花花大脸猫
    这一篇其实没有什么标准的模式,在不同的项目下,其 处理的步骤可能完全不一样,一样的是处理问题的思路,老师提供了这样一个契机展现自己处理问题的想法,但是可能每个人的想法在这个契机上并不是一样的。其实老师说的这些大部分人都会接触过,但是缺乏一个理论性的总结过程,在真实面对的时候,总是觉得不够自信面对,感觉缺少了什么,其实这就是我们所欠缺以及需要积累的。
    2022-10-05归属地:江苏
收起评论
显示
设置
留言
8
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部