重构改善既有代码设计总结之重构手法

106、提炼函数

要点:

(1)将意图和实现分开:如果需要花时间浏览一段代码才能弄清它到底在干什么,就该提炼到一个函数中,并根据期用途来命名(大多数时候根本不需要关心函数如何达成其意图,这是函数体内干的事)

(2)一个大函数中,一段代码上放着一句注释,此时可以将代码提炼到自己函数中,注释往往提示一个好名字

115、内联函数

要点:

(1)手上有一群组织不合理的函数,可以将它们内联到一个大函数中,再以合理的方式重新提炼小函数

(2)间接性可能带来帮助,但非必要的间接性总是让人晕头转向,如果系统中所有函数都是对另一个函数简单的委托,可以使用内联

119、提炼变量(引入解释性变量)

(1)意义:表达式可能非常复杂而难以阅读,这种情况下,局部变量可以帮助我们将表达式分解为比较容易管理的形式。在面对一块复杂逻辑时,局部变量使我能够给其中一部分命名,能让人更好的理解这部分逻辑

(2)如果这段逻辑在更大范围内适用,可以对新的变量使用提炼函数(106)

123、内联变量

(1)意义:在函数内部,变量能给表达式提供有意义的名字,但有时候,这个名字并不比表达式本身更具有表现力。有时候,变量会妨碍重构附近的代码。

124、改变函数声明

(1)意义:一个好的名字能让人一眼看出函数用途,不必看其代码实现(可读性),一个好的方法:先写一句注释描述函数用途,再把注释变成函数名

(2)函数参数的设置也是一样,修改参数列表不仅能增加函数的应用范围,还能改变连接一个模块所需的条件,从而去除不需要的耦合(比如,一个处理电话号码格式的逻辑,参数可以是电话号码,也可以是人,如果是电话号码,可以让逻辑无需了解“人”的概念减少模块间耦合,但如果参数用"人",可以很容易的访问后者的其他属性,当逻辑修改后,不用修改所有的调用处,换句话说提高了函数的封装度)

132、封装变量

(1)创建封装函数,在其中访问和更新变量值

(2)意义:封装提供一个清晰的观测点,由此监控数据的变化和使用情况

140、引入参数对象

要点:

(1)一组数据总是结伴同行,这样一组"数据泥团",用一个数据结构代之

(2)意义:缩短参数列表;所有用到该数据结构的函数通过相同名字来访问,提升代码一致性;更重要的是催生更深层次的改变,将围绕这些数据的共用行为组合起来提升为新的抽象概念

144、函数组合成类

(1)一组函数形影不离的操作同一块数据时,可以组建一类,把数据和行为捆绑到同一个环境中

(2)意义:减少参数的传递,简化函数调用,并且组建的对象可以方便的传递给系统其他部分

149、函数组合成变换

(1)意义:把所有计算派生数据的逻辑收拢到一处,把函数和他们的操作的数据放在一起,避免计算派生逻辑到处重复,收拢到一起也能方便查找

(2)变换:在js中相当于对象,将计算到的派生数据转为对象,java可以用函数组合成类

154、拆分阶段

(1)意义:一段代码在同时处理两件不同的事,把它拆分成各自独立的模块,当需要修改时,不需要考虑另一个主题

162、封装记录

170、封装集合

(1)意义:封装集合时人们常常犯一个错误,只对集合变量的访问进行封装,但依然让取值函数返回集合本身。这使得集合的成员变量可以直接被修改,而封装它的类全然不知。

(2)可以返回一个集合副本,或者返回一个不可变得集合代理

174、以对象取代基本类型

(1)意义:开发初期,我们往往用简单数据项表示简单情况,比如使用数字或字符串等。但随着开发进行,这些简单数据不再简单,比如,一开始用字符串表示电话号码,随后又需要格式化,抽取区号等特殊行为。这类逻辑很快占领代码库,制造重复代码,增加使用成本

(2)一旦发现某个数据的操作不仅仅局限于打印时,就为它创建一个新类,并将业务逻辑添加进去,往往能称为一个有用的工具类

178、以查询取代临时变量

(1)意义:临时变量允许我们引用之前的值,既能解释它的含义,还能避免对代码进行重复计算。但尽管使用方便,很多时候还是值得更进一步,抽取成函数,有利于我们分解冗长的函数,不必将其作为参数传递给提炼出来的小函数,也有利于在提炼得到的函数与原函数之间设立清晰边界(解耦),帮助我们避免依赖和副作用

(2)最好在类中使用,类中提供了一个共同的上下文,如果不在类中,可能会在顶层函数中拥有过多的参数,冲淡提炼函数带来的好处。

182、提炼类

(1)意义:一个类应该是一个清晰的抽象,只处理一些明确的责任。一个类如果维护大量函数和数据,往往让人不能理解,考虑将部分责任分离出去。

186、内联类

(1)如果一个类不再承担足够责任,不再有单独存在的理由,可以将该类塞进另一个类中

(2)手上有两个类,想重新安排它们肩负的职责,可以先内联类,再提炼类去分离职责会更简单

189、隐藏委托关系

(1)意义:“封装”意味着每个模块都应该尽可能少了解系统的其他部分,一旦发生变化,需要了解这一变化的模块就会比较少

(2)如果某些客户端先通过服务对象的字段得到另一个对象,然后调用后者的函数,那么客户端必须知晓这一层委托关系。如果委托类修改了接口,变化就会波及所有客户端。我们可以在服务对象上放置一个简单的委托函数,将委托关系隐藏起来,去除这种依赖

192、移除中间人

198、搬移函数

(1)意义:一个函数频繁引用其他上下文中的元素,而对自身上下文的元素却关心盛少,可以将其与更亲密的元素会和,起到封装效果,让系统别处减少对当前模块的依赖

207、搬移字段

223、移动语句

要点:

(1)将存在关联的东西一起出现,可以使代码更容易理解,比如,有几行代码取用了一个数据结构,最好让他们在一起出现,一般这项重构是"提炼函数"之前的准备工作

227、拆分循环

(1)意义:使代码结构清晰;如果一次循环做了两件不同的事,当需要修改循环时,你需要同时理解这两件事情

(2)一般拆分循环后,紧接着对每个循环提炼函数(106)

(3)关于性能,一般循环本身很少称为性能瓶颈,如果循环真的称为性能瓶颈再合并到一起

231、以管道取代循环

(1)意义:管道运算如filter、map等,代码的可读性更强,只需从头到位阅读一遍代码,就能弄清对象在管道中的变换过程

237、移除死代码

240、拆分变量

(1)意义:同一个变量承担多个不同的事情,会让阅读者难以理解

(2)场景一:同一个局部变量被赋值多次,且意义不同

(3)场景二:对输入参数赋值,变量是以输入参数的形式声明又在函数内部被再次赋值

248、以查询取代派生变量

(1)意义:对数据的修改常常导致代码的各个部分以丑陋的形式相互耦合,在一处修改数据,却在另一处造成难以发现的破坏

(2)有些变量可以很容易的随时计算出来,用计算替代变量可以消除可变性,计算常能更清晰的表达数据的含义,也能避免“源数据修改,忘记更新派生变量”

252、将引用对象改为值对象

(1)意义:如果希望几个对象之间共享一个对象,那么就用引用对象,如果不希望这个对象创建后被修改,则认为该对象是值对象

(2)值对象可以先移除所有设值函数,将字段初始化放在构造函数中,如果想改变字段的值,则重新创建一个新对象,并添加一个equal方法,判断两个值对象相等等于判断对象中的属性相等

260、分解条件表达式

(1)意义:可以突出条件逻辑,更清楚的表明每个分支的作用,提升代码可读性

(2)对条件判断和每个分支分别运用提炼函数(106)手法

263、合并条件表达式

(1)含义:有一串条件检查,检查条件各不相同,但最终行为却一致。可以使用逻辑或和逻辑与将它们合并为一个条件表达式

(2)意义:使得检查的用意更清晰(合并后的条件检查表述“实际上只有一次条件检查,只不过有多个并列条件检查而已”),其次,这项重构往往可以为使用提炼函数(106)做好准备,将检查条件提炼为一个独立函数对理清代码意义非常有用(提高代码可读性)

266、以卫语句取代嵌套条件表达式

(1)意义:给某个分支以特别的重视,卫语句告诉读者:“这种情况不是本函数的核心逻辑,如果发生了,请做一些必要的整理工作,然后退出”

(2)条件表达式通常有两种风格。第一种:两个分支都属于正常行为。第二种:只有一个分支是正常,另一个是异常情况

272、以多态取代条件表达式

意义:有时用条件逻辑本身的结构足以表达,但使用类和多态能把逻辑的拆分表述的更清晰

(1)场景一:多个不同的操作,都基于相同类型代码的switch语句,比如获取鸟的飞行速度和羽毛特征,都根据鸟类型的switch语句获取

(2)场景二:某个对象与另外一个对象大体类似,但又有一些不同之处;比如一家评级机构,对航行进行投资评级,盈利潜力分数评级和船长历史航行评级中都在询问“是否到过中国的航程”以及“船长是否曾去过中国”,那么可以用继承和多态将“中国因素”的特殊逻辑从基础逻辑中分离出来,这些重复的“中国因素”会混淆视听,让基础逻辑难以理解

289、引入特例(引入Null对象)

(1)意义:一种常见的重复代码是这种情况,一个数据结构的使用者都在检查某个特殊的值,并当这个特殊值出现时所做的处理也都相同。如果发现有多处以同样方式应对一个特殊值,我就会想把这个逻辑处理收拢到一处。

(2)做法:创建一个特殊对象,其中包括所有共用行为所对应的函数。

302、引入断言

306、将查询函数和修改函数分离

(1)任何有返回值得函数,都不应该有看得到的副作用(有种常见的优化方法,将查询结果缓存于某个字段中,这样后续的重复查询就可以大大加快,虽然这种做法改变了对象中缓存的状态,但这一修改是察觉不到,因为不论如何查询,总是活得相同结果)

(2)如果有看得到的副作用,则将查询动作从修改动作中分离出来

310、函数参数化(令函数携带参数)

(1)意义:如果两个函数逻辑非常相似,只有一些字面量值不同,可以将其合并成一个函数,以参数形式传入不同的值,从而消除重复

314、移除标记参数

(1)意义:标记参数会让人难以理解,应该怎么调用,需要调用者弄清标记参数有哪些可用值

(2)针对参数的每一种可能值,新建一个明确函数

319、对象完整性

(1)从一个结构中导出几个值,再把几个值一起传递给一个函数,可以将整个对象传递给函数

(2)可以更好的应对变化:如果将来被调用函数需要从对象中导出更多数据时,不用修改参数列表,并且可以缩短参数列表,使函数更易看懂

(3)如果很多函数在使用对象中的同一组数据,并且部分数据的逻辑重复,可以将这些处理逻辑搬移到完整对象中去

324、以查询取代参数

(1)意义:函数参数列表应该总结该函数的可变性,标识出函数可能体现出行为差异的主要方式,但参数列表应该避免重复(如果调用函数时,传入一个值,而这个值由函数自己来获取同样容易),参数列表越短,越容易理解

(2)如果可以从一个参数推导出另一个参数,那么就没有必要传递两个参数

(3)移除参数可能会给函数体增加不必要的依赖关系,需要慎重以查询取代参数

327、以参数取代查询

(1)意义:改变代码的依赖关系,让目标函数不在依赖某个元素,可以把这个元素以参数形式传递给该函数,从而将处理引用关系的责任转交给函数调用者

331、移除设值函数

(1)不希望对象创建之后,字段被修改,可以移除设值函数,将字段初始化在构造函数中完成

337、以命令取代函数

(1)与普通函数相比,命令对象提供更大的控制灵活性和更强的表达能力,还支持一些附加操作,例如撤销。而且命令对象可以拆解复杂的函数

(2)为函数创建一个空类,并考虑将每个参数创建一个字段,并在构造函数中添加对应参数

344、以函数取代命令

(1)意义:命令对象为处理复杂计算踢动强大的机制,可以轻松将原本复杂的函数拆解为多个方法,彼此间通过字段共享状态;拆解后的方法可以分别调用;但大多数时候,我只想调用一个函数,如果该函数不是太复杂,就该考虑将其变为普通函数

350、函数上移

(1)意义:避免重复代码,避免修改其中一个却未修改另一个的风险

(2)找出不同子类函数,而它们可以通过某种形式的参数调整称为相同函数,可以先应用函数参数化(310),然后上移。

(3)函数上移中,如果被提升的函数引用了子类中特有的特性,此时可以字段上移(353)和函数上移(350)将这些特性提升到超类

(4)如果两个函数工作流程大致相似,但实现细节略有差异,可以塑造模板函数,构造出相同的函数,再提升它们

359、函数下移

361、字段下移

362、以子类取代类型码

(1)表现分类关系的第一种工具是类型码字段,可能实现为枚举、符号、字符串或者数字。大多时候有这些类型码就够了,但我们可以往前一步,引入子类。继承有两个好处,首先可以用多态处理条件逻辑,如果几个函数都在根据类型码取值采取不同行为,多态就特别有用。可以用多态取代条件表达式(272)。另外,有些字段或函数只对特定类型码有意义,可以将字段下移(361)把这样的字段放在合适的子类中去。

375、提炼超类

380、折叠继承体系

(1)意义:在重构类继承体系时,我们经常把函数和字段上下移动。随着继承体系演化,发现一个类与其超类没有多大差别,把值得再作为独立的类存在,可以把超类和子类合并起来

381、以委托取代子类

399、以委托取代超类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第1章 重构,第一个案例 1.1 起点 1.2 重构的第一步 1.3 分解并重组Statemen 1.4 运用多态取代与价格相关的条件逻辑 1.5 结语 第2章 重构原则 2.1 何谓重构 2.2 为何重构 2.3 何时重构 2.4 怎么对经理说 2.5 重构的难题 2.6 重构设计 2.7 重构与性能 2.8 重构起源何处 第3章 代码的坏味道 3.1 Duplicated Code(重复的代码) 3.2 Long Method(过长函数) 3.3 Large Class(过大类) 3.4 Long Parameter List(过长参数列) 3.5 Divergent Change(发散式变化) 3.6 Shortgun Surgery(霰弹式修改) 3.7 Feature Envy(依恋情结) 3.8 Data Clumps(数据泥团) 3.9 Primitive Obsession(基本型别偏执) 3.10 Switch Statements(switch惊悚现身) 3.11 Parallel Inheritance Hierarchies(平行继承体系) 3.12 Lazy Class(冗赘类) 3.13 Speculative Generality(夸夸其谈未来性) 3.14 Temporary Field(令人迷惑的暂时值域) 3.15 Message Chai (过度耦合的消息链) 3.16 Middle Man(中间转手人) 3.17 Inappropriate Intimacy(狎昵关系) 3.18 Alternative Classes with Different Interfaces(异曲同工的类) 3.19 Incomplete Library Class(不完善的程序库类) 3.20 Data Class(纯稚的数据类) 3.21 Refused Bequest(被拒绝的遗赠) 3.22 Comments(过多的注释) 第4章 建立测试体系 4.1 自我测试码的价值 4.2 JUnit测试框架 4.3 添加更多测试 第5章 重构名录 5.1 重构的记录格式 5.2 寻找引用点 5.3 这些重构准则有多成熟 第6章 重新组织你的函数 6.1 Extract Method(提炼函数) 6.2 Inline Method(将函数内联化) 6.3 Inline Temp(将临时变量内联化) 6.4 Replace Temp With Query(以查询取代临时变量) 6.5 Introduce Explaining Variable(引入解释性变量) 6.6 Split Temporary Variable(剖解临时变量) 6.7 Remove Assignments to Paramete (移除对参数的赋值动作) 6.8 Replace Method with Method Object(以函数对象取代函数) 6.9 Substitute Algorithm(替换你的算法) 第7章 在对象之间移动特性 7.1 Move Method(搬移函数) 7.2 Move Field(搬移值域) 7.3 Extract Class(提炼类) 7.4 Inline Class(将类内联化) 7.5 Hide Delegate(隐藏「委托关系」) 7.6 Remove Middle Man(移除中间人) 7.7 Introduce Foreign Method(引入外加函数) 7.8 Introduce Local Exte ion(引入本地扩展) 第8章 重新组织你的数据 8.1 Self Encapsulate Field(自封装值域) 8.2 Replace Data Value with Object(以对象取代数据值) 8.3 Change Value to Reference(将实值对象改为引用对象) 8.4 Change Reference to Value(将引用对象改为实值对象) 8.5 Replace Array with Object(以对象取代数组) 8.6 Duplicate Observed Data(复制「被监视数据」) 8.7 Change Unidirectional Association to Bidirectional(将单向关联改为双向) 8.8 Change Bidirectional Association to Unidirectional(将双向关联改为单向) 8.9 Replace Magic Number with Symbolic Co tant (以符号常量/字面常量 取代魔法数) 8.10 Encapsulate Field(封装值域) 8.11 Encapsulate Collection(封装群集) 8.12 Replace Record with Data Class(以数据类取代记录) 8.13 Replace Type Code with Class(以类取代型别码) 8.14 Replace Type Code with Subclasses (以子类取代型别码) 8.15 Replace Type Code with State/Strategy (以State/Strategy取代型别码) 8.16 Replace Subclass with Fields(以值域取代子类) 第9章 简化条件表达式 9.1 Decompose Conditional(分解条件式) 9.2 Co olidate Conditional Expression(合并条件式) 9.3 Co olidate Duplicate Conditional Fragments (合并重复的条件片段) 9.4 Remove Control Flag(移除控制标记) 9.5 Replace Nested Conditional with Guard Clauses (以卫语句取代嵌套条件式) 9.6 Replace Conditional with Polymorphism(以多态取代条件式) 9.7 Introduce Null Object(引入Null对象) 9.8 Introduce Assertion(引入断言) 第10章 简化函数呼叫 10.1 Rename Method(重新命名函数) 10.2 Add Parameter(添加参数) 10.3 Remove Parameter(移除参数) 10.4 Separate Query from Modifier(将查询函数和修改函数分离) 10.5 Parameterize Method(令函数携带参数) 10.6 Replace Parameter with Explicit Methods(以明确函数取代参数) 10.7 Preserve Whole Object(保持对象完整) 10.8 Replace Parameter with Method(以函数取代参数) 10.9 Introduce Parameter Object(引入参数对象) 10.10 Remove Setting Method(移除设值函数) 10.11 Hide Method(隐藏你的函数) 10.12 Replace Co tructor with Factory Method(以工厂方法取代构造函数) 10.13 Encapsulate Downcast(封装「向下转型」动作) 10.14 Replace Error Code with Exception(以异常取代错误码) 10.15 Replace Exception with Test(以测试取代异常) 第11章 处理概括关系 11.1 Pull Up Field(值域上移) 11.2 Pull Up Method(函数上移) 11.3 Pull Up Co tructor Body(构造函数本体上移) 11.4 Push Down Method(函数下移) 11.5 Push Down Field(值域下移) 11.6 Extract Subclass(提炼子类) 11.7 Extract Superclass(提炼超类) 11.8 Extract Interface(提炼接口) 11.9 Collapse Hierarchy(折叠继承体系) 11.10 Form Template Method(塑造模板函数) 11.11 Replace Inheritance with Delegation(以委托取代继承) 11.12 Replace Delegation with Inheritance(以继承取代委托) 第12章 大型重构 12.1 Tease Apart Inheritance(疏理并分解继承体系) 12.2 Convert Procedural Design to Objects(将过程化设计转化为对象设计) 12.3 Separate Domain from Presentation(将领域和表述/显示分离) 12.4 Extract Hierarchy(提炼继承体系) 第13章 重构,复用,与现实 13.1 现实的检验 13.2 为什么开发者不愿意重构他们的程序 13.3 再论现实的检验 13.4 重构的资源和参考数据 13.5 从重构联想到软件复用和技术传播 13.6 结语 13.7 参考文献 第14章 重构工具 14.1 使用工具进行重构 14.2 重构工具的技术标准 14.3 重构工具的实用标准 14.4 小结 第15章 总结 参考书目 要点列表 索引

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值