重构的艺术

在吗?在吗?在吗?---》不要慌,我还在

经典重读:改善既有代码的设计

一、重构的定义

重构:对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。

重构:使用一些列重构手法,在不改变软件可观察行为的前提下,调整其结构。

二、重构的时机

1、如果你发现自己需要为程序添加一个特性,而代码结构使你无法很方便的达成目的,那就先重构那个程序,使特性的添加比较容易进行,然后再添加特性。

2、重构前,先检查自己是否有一套可靠的测试机制。这些测试必须有自我检验能力。

3、重构技术就是以微小的步伐修改程序。如果你犯下错误,很容易便可发现它。

4、任何一个傻瓜都能写出计算机可以理解的代码。唯有写出人类容易理解的代码,才是优秀的程序员。

5、事不过三,三则重构。

6、不要过早发布接口。请修改你的代码所有权政策,使重构更顺畅。

7、当你感觉需要写注释时,请先尝试重构,试着让所有注释都变得多余。

8、确保所有测试都完全自动化,让它们检查自己的测试结果。

9、一套测试就是一个强大的bug侦听器,能够大大缩减查找bug所需要的时间。

10、频繁的运行测试。每次编译请把测试也考虑进去--每天至少执行每个测试一次。

11、每当你收到bug报告,请先写一个单元测试来暴露这只bug。

12、编写为臻完善的测试并实际运行,好过对完美测试的无尽等待。

13、考虑可能出错的边界条件,把测试火力集中在那里。

14、当事情被大家认为应该会出错时,别忘了检查是否抛出了预期的异常。

15、不要因为测试无法捕获所有bug就不写测试,因为测试的确可以捕获到大多数bug。

三、代码坏味道

1)重复代码 2)过长函数 3)过大的类 4)过长参数列 5)发散式变化
6)依恋情结 7)霰(xian)弹式修改 8)数据泥团 9)基本类型偏执 10)switch惊悚(song)现身
11)冗赘(zhui)类 12)平行继承体系 13)夸夸其谈未来性 14)过度耦合的消息链 15)令人迷惑的临时字段
16)异曲同工的类 17)狎(xia)昵关系 18)中间人 19)纯稚的数据类 20)不完美的库类
21)过多的注释 22)被拒绝的遗赠

四、重构的方法

大的分类共用7种类型  1)重新组织函数;2)在对象之间搬移特性;3)重新组织数据;4)简化条件表达式;  5)简化函数调用;6)处理概括关系;7)大型重构;

1、重新组织数据

1)自封装字段(不要直接访问一个字段,请使用取值和设置函数)

76600b00b744a514f2d692624f3dff3f.jpeg

c95bf7e7c56ca2a2212665a30bb17812.jpeg

d17e37cae4e74cb9701ccb560a709202.jpeg

2)以对象取代数据值

6d6ef27ddd0231272b418ff6c78eb355.jpeg

3)将值对象改为引用对象(享元模式??)

29d20b972948579a45a08fd1fb78726a.jpeg

4)将引用对象改为值对象(上一条的反向动作)

3cb73ff98a944f032358c45388929bd0.jpeg

5)以对象取代数组(更易读)

9917f8970efce8dc9c41f1e8f9f3622a.jpeg

710f0925ed35fcff5bb478712d5d5ea0.jpeg

6、将单向关联改为双向关联(就像双向链表,某些情况下有奇效)

15dac8ecbd624a1e76ca27c1a19a7e07.jpeg

0c139d5c938363cf5a9193157f124b59.jpeg

7、将双向关联改为单向关联(降低复杂度)

9bb3e5612f7a89893ea38ee0e2fd2d12.jpeg

9327a07290b738f8870da46e22731d52.jpeg

8)以字面量取代魔法数(常量的定义)

64f40e52a336a73f7a5943713048aafd.jpeg

9)封装字段,面向对象三大特性之一:封装性

36f2821c65c45e8c3d9170599e8a4576.jpeg

10)封装集合(用起来更方便)

74a999de23229e2f8fe19c51fd38e205.jpeg

2、在对象之间搬移特性

1)搬移函数(将函数放到最合适的位置)

74b0498f0630cd1d5ee916cec7ab70f2.jpeg

2)搬移字段(将字段放到最合适的位置)

59e397f6cc2b4ac36546edbf549d1123.jpeg

3)提炼类(类的单一职责原则)

ff1bc5992af6818eabec96fe8b99ddb4.jpeg

4)将类内联化(与提炼类 相反)

208e89322d1507bb0b7294e244dca07e.jpeg

5)隐藏“委托关系”

(兄弟们这不就是 迪米特法则吗,不要和陌生人说话,这就是中介者模式啊)

a87ef1410b0d6875e769ebfa75da3338.jpeg

6)移除中间人

(这个就是和解除”委托关系“的意思,大量的委托也是有缺点的,所以要根据不同场景来灵活运用)

624eb6fcc9b1a86fbac8b3e2a802983e.jpeg

ba2cb38b27426b4cdda1cd60c48af856.jpeg

7)引入外加函数(在客户类中增加一个额外函数)

c3e3e6754cffd2650d1ce7321c47ee02.jpeg

8)引入本地扩展

本质就是对已有类库进行扩展,两种方式  1)继承,在子类中增加额外扩展方法   2)使用组合的方式扩展(组合优于继承原则,装饰器模式、包装器模式、代理模式)

00e1e21edd8a5a3e0582544e5c801d3f.jpeg

3、重新组织函数

1)提炼函数,这可能是最常用的重构手段之一:好处1)每个函数都像一个注释,有助于理解高层代码2)被复用的机会更大 3)函数的覆写更容易

dd7f8d22bd2b456d0b9d88c8ab5276ab.jpeg

2)内联函数(提炼函数的反向操作,用的机会不多)

40c62e5b422b6cb45853c6d5f0d0afa9.jpeg

3)内联临时变量,可以减少代码行数

239a68d3c603b852b0a8819d9ff0adab.jpeg

4)以查询取代临时变量,提高代码复用的概率,但可能存在重复计算的性能问题,要根据实际情况选择

f9610e6303682f62f63349b77494a875.jpeg

5)引入解释性变量,提高代码可读性

3560b9978c9d241d3625ca0f3d6b44cc.jpeg

6)分解临时变量,临时变量被多次赋值,代码可理解性下降,出错概率会增加,还有入参一般都声明为final就是为了防止被重新赋值,导致出错概率增加

f0ef1e05ec16cd0a437917b29e90c125.jpeg

7)移除对参数的赋值,大佬写的代码,入参往往是final修饰的(一般用在长函数和复杂函数中,简单函数可以不考虑)

 1)以临时变量取代对参数的赋值动作 2)为参数加上final修饰,强制遵守“不对参数赋值”的惯例

07054b9a3cd349c2229a108aa0baf7b3.jpeg

8)以函数对象取代函数,主要应对局部变量过多,提取函数需要过多参数的情况

675c3ebd96ae5cf6827b62d43f161b8e.jpeg

0bd273d755697102e4b3736626c5cf96.jpeg

604b80ad02dd171a3f1fde0ef4579384.jpeg

9)替换算法

5b767a892df9974753963276a15388bf.jpeg

替换一个巨大而复杂的算法是非常困难的,只有先将它分解为较简单的小型函数,然后你才有把握进行算法替换工作。

4、简化条件表达式

1)分解条件表达式(提取函数呗)

59a2e527e418561ec21b31fa94754ab5.jpeg

2)合并条件表达式(提取函数呗)

195689a7afe491de3435a6c081abba5a.jpeg

3)合并重复的条件判断(逻辑想明白了才能写出更简洁的代码)

ae9d2a651339324087e351946584795c.jpeg

4)移除控制标志

96fff3ac120e355f1a6bb18d052d0516.jpeg

b94598d9e581e410e4f24dbdbc582d5f.jpeg

ca606c23f47a463f5bb69af96bced09a.jpeg

重构为:

6de9653114846a4bf74f93e787d0a644.jpeg

bcda8637c582cd293230e41a4709781e.jpeg

重构为:

8e7d1a164a4d50c4fb5dc483c764d90b.jpeg

5e912277801ff9b0095f06978d74d3fc.jpeg

5)以卫语句取代嵌套条件表达式(显然可读性更强)

096ab98dbd10f48ebe4bd755ca1a6dda.jpeg

6)以多态取代条件表达式(策略模式)

80332a8f84196ff989575d2e734bbd7c.jpeg

7)引入Null对象

fa7a58f4b55ef2a9de418e9fb39ecfa2.jpeg

8)引入断言

d5959b55b8182be916437acefecfd68d.jpeg

5、简化函数调用

1)函数改名  rename method

a3f8555573b871bfe4788ccd99d7ec41.jpeg

2)添加参数,增加参数的个数或者用对象替换参数

e6a28911d39064fe79017c6090e842f3.jpeg

3)移除参数

97ac0cb9b29ea19eacf76b0b3568b683.jpeg

4)将查询函数和修改函数分离(避免在一个查询函数中进行修改操作,函数职责要单一)

7cbd80d055324a86061fa3589bfbe06d.jpeg

2b0639647eee511bb7e855efc7e2e0eb.jpeg

5)令函数携带参数(合并类似工作的函数)

f45f395083dbd48b0bc3f47fa98743f6.jpeg

f4fb39ec4f2ec56c6e88a5f142242e15.jpeg

6)以明确函数取代参数(和上一条“令函数携带参数”相反的操作,用在参数化并不能增加代码复用度的情况,这时候还不如分离开)

818a515f50163e2b369c15ea14d17f8b.jpeg

7)保持对象完整

a1235b49eab05c7791bf95f23ce33069.jpeg

330af7a0a387309a7882d9897e66cf24.jpeg

83792f8f6175f029b6dd72ffcd0c6562.jpeg

8)以函数取代参数(减少不必要的传参)

35fab1f3aa8f061b220b287fb31a2d20.jpeg

9)引入参数对象(同时参数相关的行为也可以转移到参数对象中)

12c56bfe710bafb7f29ff99ce1eaf253.jpeg

b644c207f91eb8d60993c4851cd7e6d1.jpeg

b900dde88985eebca1979bd80cefc9ef.jpeg

重构之后的效果:将参数提取到 DateRange对象中,并将相关行为 提取到DateRange对象中。

0d2c4fd9f381fde8ed537c41962a3d0b.jpeg

352f51256c31a7896d33dfef82c7c11b.jpeg

b8f177c791970ab09178aa965cc30bc5.jpeg

10)移除设值函数(当对象一旦创建后不希望再改变其内容,同时将字段设值为final类型,就将设值函数移除或者隐藏)

32fbba9e65dabf450944ae3c2ab3eab4.jpeg

11)隐藏函数

d3db91971e7d2184e2fac4cd6798aab0.jpeg

12)以工厂函数取代构造函数

bf08dc7f4c4fa4b82757caa6c219318c.jpeg

1b6ffd76422ced416996d658c39e67a7.jpeg

引入工厂函数

7edcb7268023f1093f0f2ba6a5dcdc00.jpeg

改进工厂函数

0b2655dec26f920529f0a05c05a139ee.jpeg

进一步改进工厂函数

055253fe6923301b23aae4fb1e2bff7a.jpeg

以明确函数创建子类来代替工厂函数

3249d7bf511c48fc8f3f404f4b3e3996.jpeg

13)封装向下转型

3d6cb27c1aa6bee551c69100faa883fa.jpeg

14)以异常取代错误码

5c8ac7e70a25fe8a5ec82599595cf1cf.jpeg

15)以测试取代异常(提前校验参数的合法性)

a51197a99d2a62e4b4808ef483a117e1.jpeg

6、处理概括关系

1)字段上移

b5b0655093b4cbf3e4f85a2f0fe30a35.jpeg

2)函数上移

b5a25858e38f48e21f6f16bc31b53209.jpeg

3)构造函数本地上移

4f2fbc36c659f4d3ac56b18cf9a02caf.jpeg

4)函数下移

860c401fa95f67c11fe98ce1af2508ba.jpeg

5)字段下移

fd1c230ac10d34804a9cd8bbc6f85172.jpeg

6)提炼子类

ceae8b9fd6f31a5049c2de9ec93e4fd2.jpeg

7)提炼超类

1839c0ca16e1c56790ffa4023e8faf90.jpeg

8)提炼接口

9b834d3166ae06dab40ec5b4fe80100b.jpeg

9)折叠继承体系

94953303a058afa797bc14782dee281c.jpeg

10)塑造模板函数(模板方法设计模式啊)

8786fabfe556db85ed4529c7713fa5ef.jpeg

11)以委托取代继承

94cc053cfc1aff80c00e292606ae047c.jpeg

12)以继承取代委托

557285e7845f92eda9785e9160d2df44.jpeg

7、大型重构

1)梳理并分解继承体系

58e910e1d72584cf9b3676755ba62334.jpeg

2)将过程化设计转化为对象设计

7a2b5398774630f16e85f487a6365cdd.jpeg

3)将领域和表述/显示 分离(MVC设计模式)

4)提炼继承体系

0553d1681c8eee90a5164f8afe2caa7c.jpeg

四、重构的本质

就是使程序代码质量更高,可读性更强,应对需求变化的能力更强,修改扩展更轻松,延长软件的生命周期,创造更多的价值。

五、重构的阻碍

1、你不知道如何重构;

2、如果这些利益是长远的,何必现在付出这些努力呢?长远看来,说不定当项目收获这些利益时,你已经不在职位上了;

3、代码重构是一项额外工作,老板付钱给你,主要是让你编写新功能;

4、重构可能破坏现有程序;

六、重写、补丁与重构

重写整个程序是一种极端,一直凑合打补丁是另一种极端。

中庸之道是在软件开发与演进过程中及时的进行重构,消除代码坏味道与架构设计的不合理之处。‍

-----------》来自微信公众号 “老吕架构” 《----------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吕哥架构

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值