1、重新组织函数
1.1 提炼函数 Extract Method
* 概述:将一段代码放入一个独立函数中,并让函数名称解释该函数的用途。
* 动机:厌恶过长的代码。如果函数过长,则很难被理解(用途不清晰,需要大段的注释描述意图),测试也困难;相反,如果一个函数够小、有清晰意图的命名,则被复用的机会更大,函数也更容易被修改。
* 做法:编写一个新函数,根据这个函数的意图来命名 -- 如果能对一段代码给予一个更好表达其意图的命名,都可以提炼她。
* 注意:这个重构手法最困难的地方在于提炼局部变量:
1. 如果需要读取源函数的局部变量,则可将其作为参数传入新函数。
2. 如果某些局部变量需要在目标函数后面使用,可能需要新函数返回该局部变量改变的值。
3. 对于需要修改多个局部变量的情况,则可不提炼此段代码,或者将所有临时变量抽取为一个类。
1.2 内联函数 Inline Method
* 概述:在函数调用点插入函数本体,然后移除该函数
* 动机:1.有些函数的内部代码比函数名本身更加清晰易读,则可以内联。2.函数调用混乱,可以先全部内联在一起,然后重新提炼。
* 做法:检查多态覆盖,检查函数引用,引用点直接替换为函数。
1.3. 内联临时变量 Inline Temp
* 概述:将所有对该变量的引用动作,替换为对她赋值表达式。
* 动机:临时变量可能妨碍其他的内联手法。
* 做法:确保只被一个简单的表达式赋值了一次
1.4. 以查询取代临时变量 Replace Temp with Query
* 概述:程序以某个临时变量保存表达式的运算结果。则将一个表达式提炼到一个函数中,将将对这个临时变量的引用替换为对新函数的调用
* 动机:临时变量总是驱使你写更长的函数,因为临时变量只在函数内可以访问到,所以将一些运算得到的临时变量提炼为一个函数,可以在任何时候访问。ps: 导致重复计算,但是使代码清晰。
* 做法:确保该临时变量只被赋值一次,然后用新函数替换引用处。可以先将该临时变量声明为final,让编译器保证只赋值一次。
ps: 1,2,3,4的操作都让代码结构变得更加清晰、简洁,使得函数名称本身便具有自注释功能。
1.5. 引入解释性变量 Introduce Explaining Variable
* 概述:将复杂表达式(或其中一部分)的结果放入一个临时变量,以这个变量的名称来解释表达式用于
* 动机:将一个复杂的表达式进行拆解,用临时变量的名称来表达部分过程的意图。
1.6. 分解临时变量 Split Temporary Variable
* 概述:程序中某个临时变量有多个用途,既不是循环变量,也不是收集计算结果变量,则应该对每个用途编写一个临时变量
* 动机:临时变量有不同的用途,或者由不同含义的操作计算而得到,则不应该使用同一临时变量。
ps: 5,6的拆解都是为了使得表达式的意图更加清晰,各种复用临时变量,复杂计算过程等代码都可能让代码变得含混。
1.7. 移除对参数的赋值 Remove Assignments to Parameters
* 概述:代码对一个参数进行赋值,应该用一个临时变量取代参数的位置。
* 动机:对参数的赋值可能改变其引用的对象(引用参数), 从而丢失了原来的入参引用。所以将参数赋值给一个临时变量以改变这种含混性。
1.8. 以函数对象取代函数 Replace Method with Method Object
* 概述: 有一个大型函数,其中过多的局部变量无法进行函数提炼,则将这个放入一个单独对象中,这样局部变量就成了对象的成员变量,然后在对象内将大型函数分解为多个小函数
* 动机: 局部变量太复杂,根本无法拆解, 则使用一个对象来描述该方法。 -- 该对象需要有一个与方法意图对应的命名。
* 做法: 1. 建立一个新类,并将原类作为该类的一个常量引用
2. 新类中提供构造函数接收原对象,及原函数的所有参数。
3. 在新类中提供一个函数,将原函数的代码复制到其中,通过原对象引用相应变量。
1.9. 替换算法 Substitute Algorithm
* 概述:将函数本体替换为另一个算法
* 动机:原函数的算法实现比较复杂,切不清晰,或者性能不好。如果你发现有更好的实现方式,勇敢的壮士断腕,替换该算法吧。
2、在对象间搬移特性
2.1. 搬移函数 Move Method
* 概述:将一个函数移动到另一个类中去
* 动机:类中一个函数与其他的类进行频繁的交流,而与本类交流较少,则将函数移到另一个类中,从而减少调用关系。
* 做法:搬移一个或者一组相关的函数;检查源类的子类和超类是否有该函数的其他声明(覆盖,委托等问题);修改源函数使之成为一个委托函数。
2.2. 搬移字段 Move Field
* 概述:将类中一个字段搬移到其他类中
* 动机:本类中的一个字段,被其他类中的字段使用次数更多,搬移字段减少调用关系,间接提高信息隐藏性。
* 做法:如果字段被很多地方引用,则先使用自我封装(Self-Encapsulation)将字段变成设/取值函数,然后搬移字段,最后修改设/取值函数为一个委托。
2.3. 提炼类 Extract Class
* 概述:建立一个新的类,将相关的字段和函数从旧类中搬移到新类
* 动机:1. 一个类的方法过多、责任过大做了由两个类做的事情。主要是由于当前类对于场景的抽象不够细致,导致过于复杂。
2. 一个类中的数个特性朝着不同方向发展,从而变得无所关联。最主要的表现是子类化,某些特性需以一种方式子类化,某些特性以另一种方式子类化
* 做法:从一个类中搬移函数和字段到一个新类中,使得新类作为一个事物的更细致抽象。
* 例如:一个person类有地址字段和函数,比较复杂。可以将地址抽象为一个新类,然后person调用这个地址类。
2.4. 内联类 Inline Class
* 概述: 将一个类的所有特性搬移到另一个类中,然后移除该类
* 动机: 这个类没有做太多事情
* 做法: 上述的反过程
2.5. 隐藏委托关系 Hide Delegate
* 概述: 在服务类建立客户所需的所有函数,用以隐藏委托关系
* 动机: 对内部进行封装,通过一个委托类来调用其他对象,这样调用者可以不用了解内部变化,从而降低修改风险。
* 做法: 正如设计模式中的代理模式等, 不仅隐藏调用关系,还可以对调用进行统计等管理。
2.6. 移除中间人 Remove Middle Man
* 概述: 直接调用受托类
* 动机: 某个类做了过多的简单委托,每添加一个新特性都需要在委托类中进行相应添加。
* 做法: 上述反过程
2.7. 引入外加函数 Introduce Foreign Method
* 概述: 在客户类中建立一个函数,并以第一参数形式传入一个服务类实例
* 动机: 由于代码修改权不在自己手里,但是需要为某个类添加新特性。可以在外层写这样一个函数,并所需要的值都以参数传入。
* 做法: 在拥有代码所有权后,将此函数返回她该去的地方
2.8. 引入本地扩展 Introduce Local Extension
* 概述: 当需要为一个服务类提供一些额外的函数,但是你无法修改。可建立一个新类,使她包含这些额外的函数。让这个扩展类成为源类的子类或者包装类
* 动机: 为类做扩展,可以使用装饰模式或者继承,使用继承请确保必定有继承关系。
ps: 从以上重构方法可以看出,其最终都是为了减少了代码中的调用关系,提高代码的意图表达。
3、 重新组织数据
3.1. 自封装字段 &封装字段 &封装集合 Self Encapsulate Field & Encapsulate Field & Encapsulate Collection
* 概述:将字段进行封装,然后提供设/取值函数,对于集合取值只能返回可读的副本
* 动机:隐藏内部数据接口,这样就算字段数据结构发生变化,只需要调整设/取值函数,从而不影响其他类的调用; 对于集合需要防止外部对引用的修改; 方便上syncrized关键字
3.2. 以对象取代数据值 Replace Data Value with Object
* 概述:将数据项变为字段
* 动机:数据项为对一个事物属性的描述,你无法估量这个场景下该事物属性的复杂性,所以用一个类构建该数据项,能够抗击未来的变化。
3.3. 将值对象改为引用对象 Change Value to Reference
* 概述:将这个值对象变成引用对象
* 动机:对一个对象修改的数据,能够影响引用这个对象的地方
3.4. 将对象引用改为值对象 Change Reference to Value
* 概述:将引用对象变为一个值对象
* 动机:利用其不可变性质
3.5. 用对象取代数组 Replace Array with Object
* 概述:以对象替换数组,对于数组中的每个元素,以一个字段来表示
* 动机:有些其他语言,将一个对象的属性存放在一个数组中了。将其重构为对象,从而可用字段名逐一描述
3.6. 复制被监视数据 Duplicate Observed Data
* 概述:将数据复制到一个领域对象中,建立一个observer模式,用于同步领域对象和GUI对象内的数据
* 动机:分离界面显示和业务逻辑,在界面更新数据的时候同步更新领域对象数据
3.7. 将单向关联改为双向关联 Change Unidirectional Association to Bidirectional
* 概述:添加一个反向指针,并使修改函数能够同时更新两条连接
* 动机:使得两个类相互依赖,方便访问对方的成员。 虽然方便,但是调用关系复杂,容易产生僵尸对象,修改时相互影响。
3.8. 将双向关联改为单项关联 Change Bidirectional Association to Unidirectional
* 概述:去掉不必要的关联
* 动机:降低类间复杂度
3.9. 以字面常量取代魔法数 Replace Magic Number with Symbolic Constant
* 概述:创造一个常量,根据其意义命名,并将有特殊意义的字面数值替换为这个常量
* 动机:魔数-有特殊意义的数字,使用一个常量来表示这个数字的意义
3.10. 以数据类取代记录 Replace Record with Data Class
* 概述:面对传统编程的记录结构,为该记录创建一个'哑'数据对象
* 动机:与数据库的记录进行交互,ORM
3.11. 以类取代类型码 Replace Type Code with Class
* 概述:用一个新的类替换该类中用常量数值表示的类型
* 动机:其实就是使用枚举类型来代替常量枚举,这样使得类型被更有意义的描述,并且提供了对应的操作函数,对于复杂的类型十分方便。
3.12. 以子类取代类型码 Replace Type Code with Subclasses
* 概述:为类型码建立一个继承宿主类的子类,将根据这个类型码执行的操作,放入到一个子类中去。
* 动机:将不同类型码的操作都放入对应的子类中,这样不经让代码更清晰,同时防止错乱。
3.13. 用状态或者策略取代类型码 Replace Type Code with State/Strategy
* 概述: 类中有类型码会影响类的行为,但是无法通过继承手法来消除
* 动机: 无法直接继承宿主类,则自己定义一个抽象的类去继承,与上述一致。
3.14. 以字段取代子类 Replace Subclass with Fields
* 概述: 各个子类的差别不是打,只在一些常量数据上
* 动机: 将子类中的共同行为搬移到超类,并在超类中定义这组差异的常量。减少无必要的编码
4、 简化条件表达式
4.1. 分解条件表达式 Decompose Conditional
* 概述:有一个复杂的条件表达式,从if then else中分别提炼出独立函数
* 动机:复杂的条件逻辑常常导致代码复杂度的提高,提炼不同分支,并按照意图命名,是代码可读性和清晰提高
4.2. 合并条件表达式 Consolidate Conditional Expression
* 概述:有一些列的条件测试,但是都返回相同结果,则合并这些表达式
* 动机:有一些表达式虽然条件不同,但是结果一样,将这些条件合并
4.3. 合并重复的条件片段 Consolidate Duplicate Conditional Fragment
* 概述:条件表达式的每个分支有相同的一段代码,将这些代码抽取出来,放入表达式之外
* 动机:将每个分支都执行的代码,搬移到代码执行分支之外,简化分支表达式,以使代码更加清晰。
4.4. 以卫语句取代嵌套条件表达式 Replace Nested Conditional with Guard Clauses
* 概述:用卫语句处理特殊情况,卫语句就是if then return这种形式的表达式,常用于函数入口处,保护函数体只接受正确的参数。
* 动机:对于情况比较特使的逻辑,使用此表达式可以简化代码,使之更加清晰
4.5. 以多态取代条件表达式 Replace Conditional with Polymorphism
* 概述:将条件表达式的分支,放入每个子类的覆写函数中,并将原始函数声明为abstract
* 动机:子类覆盖超类的条件表达式,在子类中只关注与自己相关的条件和行为。这样将集中在一起的逻辑打散到各个子类中。
4.6. 引入Null对象 Introduce Null Object
* 概述:当需要再三检查对象是否为null,则将null值替换为一个null对象
* 动机:为原类建立一个子类,其行为就是原类的null版本,可以方便多态的进行。空对象的存在,可以省去判空逻辑(判空逻辑只写在Null对象内部,其他写在超类)。
4.7. 引入断言 Introduece Assertion
* 概述:某段代码需要对程序状态做出某种假设,以断言明确表现这种假设
* 动机:断言可以帮助阅读程序代码所做的假设,可以在调试和调试,测试中广泛使用
ps: 这些方法都为了减少条件表达式的复杂性,提高其清晰度和意图,使用多态和空对象的方式,在一定程度上为编程提供方便。
5、 简化函数调用
5.1. 函数改名 Rename Method
* 概述:修改函数名称
* 动机:函数的名称未能表示函数的意图
5.2. 增加参数 Add Parameter
* 概述:为函数添加一个对象参数,让这个对象带进函数所需的信息
* 动机:某个函数需要从调用端获取更多的信息
5.3. 移除参数 Remove Parameter
* 概述:将函数参数移除
* 动机:函数本体不在需要某个参数,而参数列表却有
5.4. 将查询函数和修改函数分离 Separate Query from modifier
* 概述:建立两个不同的函数,其中一个负责修改,另一个负责查询
* 动机:获取值得时候会修改函数值,会使得其他只需要取状态值得调用者关心更多的东西,从而产生副作用
5.5. 令函数携带参数 Parameterize Method
* 概述:建立单一函数,以参数表达那些不同的值。
* 动机:多个函数做着类似的工作,只是因为少数几参数,或者参数个数不同。则可以使用一个单一函数将他们统一起来,为这个函数增加携带参数
5.6. 以明确函数取代参数 Replace Parameter with Explicit Methods
* 概述:针对该参数的每一个可能值,建立一个独立函数
* 动机:有一个函数,取决于不同的参数值而采取不同的行为,这时可以为每个独立之建立一个独立函数。 某种程度与上一条相反
5.7. 保持对象完整 Preserve Whole Object
* 概述:改为传递整个对象
* 动机:从某个对象中取出若干值,并将他们作为某一次函数调用参数的时候,可以将参数修改为传递整个对象,以减少参数列表和对抗未来改动的风险。
5.8. 以函数取代参数 Replace Parameter with Methods
* 概述:让参数接受者去除该项参数,并直接调用前一个函数
* 动机:如果函数可以通过其他途径取得参数值,那么久不应该通过参数值获取。这样可以减少参数列表,从而减少局部变量。
5.9. 引入参数对象 Introduce Parameter Object
* 概述:以一个对象取代这些参数
* 动机:某些参数总是很自然的同时出现,当一组参数总是被同时传递给多个函数时候,则将这些参数整合成一个对象
5.10. 移除设置函数 Remove Setting Method
* 概述:去掉字段中的所有设置函数
* 动机:对象中的某个字段只应该在对象创建的时候被设置,然后就不再改变,则不因该提供设值函数,否则可能导致其值被修改,且容易混淆
5.11. 以工厂函数取代构造函数 Replace Constructor with Factory Method
* 概述:将构造函数替换为工厂函数
* 动机:通过静态工厂函数来生产对象,从而将对象生产权利把控在本类中,调用者完成不需要关系这个新对象的构建过程
5.12. 封装向下转形 Encapsulate Downcast
* 概述:将向下转形动作移到函数中
* 动机:某个函数返回的对象需要调用者自己强转类型。这是可以将强转类型封装在函数内部,使得调用者无需关心实际类型。需要该函数返回类型有限且确定。
5.13. 以异常取代错误码 Replace Error Code with Exception
* 概述:将错误码改用异常
* 动机:抛出异常能够更加清楚知道代码执行过程产生的问题。
5.14. 以测试取代异常 Replace Exception with Test
* 概述:修改调用者,使它在调用函数之前先做检查
* 动机:异常只应该用于哪些产生意料之外的错误行为,而不应该成为条件检查的替代品。可以提供一个可重复执行的测试函数,让调用者者调用前先检查某个条件,在测试函数中处理try catch
ps: 从上述方法可以看出,其最终都是为了减少函数间传参的复杂度,从而减少了局部变量个数,也就提高的代码清晰度。
6、处理概括关系
6.1. 字段上移 Pull Up Field
* 概述:将字段移到超类
* 动机:两个子类拥有相同的字段
函数上移 Pull Up Method
* 概述:将函数移动至超类
* 动机:有些函数各个子类中产生完全相同的结果
6.3. 构造函数本体上移 Pull Up Constructor Body
* 概述:在超类中新建一个构造函数,并在子类中调用她
* 动机:各个子类中拥有本体几乎一致的构造函数
6.4. 字段下移 Push Down Field
* 概述:将这个字段移动到需要她的类中
* 动机:超类中某个字段只被部分子类用到,让数据与操作在同一个类中。
6.5. 提炼子类 Extract Subclass
* 概述:新建一个子类,将只被某些实例用到特性移动该子类中
* 动机:子类划分不够具体,不够细致,导致某个类包含过多本不该自己管理的东西,这是可以提炼一个新的子类。
6.6. 提炼超类 Extract Superclass
* 概述:为两个类建立一个超类,将相同的特性移至超类
* 动机:两个类具有相似的特性
6.7. 提炼接口 Extract Interface
* 概述:将相同的子集提炼到一个独立接口中
* 动机:1. 若干客户端使用类接口中的同一子集;2. 两个类的接口有部分相同。这两种情况都可以抽出一套共有接口。
6.8. 折叠继承体系 Collapse Hierachy
* 概述:将继承体系合为一体,以消除继承体系
* 动机:超类和子类,并没有太大的区别。 ps:
6.9. 塑造模板函数 Form Template Method
* 概述:模板模式,不再概述
6.10. 以委托取代继承 Replace Inheritance with Delegation
* 概述:装饰模式,不再概述
* 动机:没有继承关系的两个类使用继承,不仅没有代码复用,还继承了父类大堆不相干方法,容易造成混淆;对于抽象的方法还必须覆写。而装饰模式可以自主选择需要复用的方法。ps:非继承关系的扩展请使用装饰模式;有继承关系的扩展请使用继承
6.11. 以继承取代委托 Replace Delegation with Inheritance
* 概述:对于有继承体系的仍然使用继承体系,这样可以复用父类的方法和字段。
7、大型重构
7.1. 梳理并分解继承体系 Tease Apart Inheritance
* 概述:建立两个继承体系,并通过委托关系让其中一个可以调用另一个
* 动机:某个继承体系同时承担两项责任,将此继承体系拆解,使得抽象的分类更清楚、细致从而提高类的复用率,并使代码更简洁。
7.2. 将过程设计转化为对象设计 Convert Procedural Design to Objects
* 概述:将数据记录变成对象,将大块的行为分成小块,并将行为移入相关的对象中
* 动机:将面向过程的代码重构为面向对象的风格。
7.3. 将领域和表述/显示分离 Separate Domain from Presentation
* 概述:将领域逻辑分离出来,为他们建立独立的领域类
* 动机:从GUI中抽离领域逻辑,从而做到与显示的分离。如MVC模式
7.4. 提炼继承体系 Extract Hierachy
* 概述:建立继承体系,以一个子类表示一种特殊情况
* 动机:有一个类做了太多的工作,其中一部分是大量的条件表达式完成的。
标签:动机,重构,函数,方式,对象,子类,一个,概述 From: https://www.cnblogs.com/use-D/p/18168492