重构-改善既有代码的设计总结

本文详细介绍了软件开发过程中常用的重构方法,包括重写与重构的区别、重构的原则、以及如何通过具体的技术手段来改善现有代码结构,比如Extract Method、Move Method、Change Unidirectional Association to Bidirectional等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

随着项目的进展,我们会发现已经完成的代码结构不再象我们预想的那么清晰合理,这大多是由于需求的变更造成的。此时我们需要改善目前的代码,有两种方法,一种是重写,另一种是重构。重写的好处是明显的,结构必然更合理,但是问题在于浪费时间。重构是一个折中的方法,时间始终,调整后结构也变得更合理。一般情况下,当我们对已有代码修改超过三次时就需要重构。

重构的基本原则是步步为营,每次进行一小步,然后测试,周而复始。另一个原则是抵制诱惑,不要改变代码的外在表现,不要试图修正代码中的Bug。下面是具体的重构名录:

 

重新组织你的函数
Extract Method:将太长的函数从逻辑上分成几个小函数
Inline Method:将几个没有实质性工作的小函数合并
Inline Temp:将仅赋值一次的变量去掉,用到它的地方统统用所赋的值代替
Replace Temp with Query:和上类似,当所赋值为一个复杂的表达式时用一个函数实现此表达式,用到的地方用函数代替
Introduce Explaining Variable:当某一表达式(特别是复杂的逻辑表达式)时,将其拆成几部分分别赋给几个命名规范的变量
Split Temporary Variable:将复用的变量用几个变量表示,使得其更易理解
Remove Assignments to Parameters:当对函数的传入参数赋值时,用另一个临时变量代替,避免语义混淆
Remove Method with Method Object:当某个复杂函数由于内部参数太多以至于很难Extract Method时,可以用一个类封装他,使用类的内部成员代替这些参数
Substitute Algorithm:将函数采用的算法换成另一个算法,同时保持函数原型不变

在对象之间搬移特性
Move Method:当某个类成员函数和另一个类耦合过紧时可以考虑将其移动到另一个类中
Move Field:当某个类成员变量被另一个类大量使用时,可以考虑将其移动到另一个类中
Extract Class:当一个类完成了多个不相关的功能时,可以考虑将其分解成几个类
Inline Class:当一个类没有完成什么实质性工作时,可以考虑将其合并到其他类中
Hide Delegate:当外部需要通过一个类获取另一个类的指针,然后再调用另一个类中某个方法时,委托关系就过于复杂,可以考虑在这个类中这节定义一个完成另一个类中此函数功能的函数。
Remove Middle Man:和上述情况完全相反,当为了隐藏代理而使得这个类过于庞杂时,就需要恢复原样
Introduce Foreign Method:当你需要为某个无法修改的类添加函数时,可以考虑在其使用类中添加
Introduce Local Extension:和上述情况类似,但是采用另外一个解决方案——增加一个子类,在子类中增加此函数

重新组织数据
Self Encapsulate Field:将变量的访问与设置封装,仅使用封装后的函数访问变量
Replace Data Value with Object:当为变量定义了很多的行为后,需要考虑将他们连同行为封装到一个对象中去
Change Value to Reference:当类的实例对象可以共享时,类似于享元模式,需要考虑将值对象改成引用对象。可以用一个hash表保存已创建的实例,仅在表中没有的实例才创建,同时添加到hash表中
Change Reference to Value:和上述相反,某些情况下使用引用对象会使得内存区域错综复杂,特别对于分布式系统和并发系统。此时对于小巧而不变的对象可以考虑将其改成值对象
Replace Array with Object:当有一个数组,其元素代表不同的东西时,可以考虑将其转变成一个对象
Duplicate Observed Data:当某些逻辑数据保存在界面类中时,而逻辑类中的函数又要访问它们,此时可以考虑将其赋值到逻辑类中,使用Observer模式保存其内容的同步
Change Unidirectional Association to Bidirectional:两个类之间只有一条单向关联,当你发现有需求要求反向操作时,就可以两条反向关联
Change bidirectional Association to Unidirectional:两个类之间有双向关联,但如今某个类不再需要另一个类的特性时,可以将这条关联删除
Replace Magic Number with Symbolic Constant:用符号常量取代魔法数。魔法数指那些有特殊意义的常量数值,如PI,e,等等
Encapsulate Field:对于类中的公有数据成员,将其改成私有的,然后提供相应的访问函数
Encapsulate Collection:对于返回结合(array,list,stack等)的函数,将其改写成返回常引用的函数和增加、删除结合成员的函数
Replace Record with Data Class:将记录封装成哑数据类
Replace Type Code with Class:将枚举型数据封装成数据类,类的实例只有有限个,每个代表一个枚举常量
Replace Type Code with Subclass:当某个不可变的枚举数据影响到类时,可以考虑用子类代替这个枚举数据
Replace Type Code with State/Strategy:当某个枚举数据影响到类,并且无法子类化时,可以使用状态模式或者策略模式对其封装
Replace Subclass with Fields:当子类之间的唯一差别仅在返回常量数据时,可以在父类中增加一个值域,然后将子类删除

简化条件表达式
Decompose Conditional:将某个复杂的条件语句分解,if条件、then部分和else部分各自单独封装成一个函数
Consolidate Conditional Expression:当一系列条件测试命中后执行的代码都相同时,将这些条件合并,然后封装到一个独立的函数中去
Consolidate Duplicate Conditional Fragments:当条件表达式的不同分支中存在相同代码时,可以将它们分离出来,搬移到条件表达式外部
Remove Control Flag:当代码中出现大量的用户控制流程的标记变量时,可以尝试使用continue、break或者return来取代这些标记
Replace Nested Conditional with Guard Clauses:当某个条件语句过于复杂时,可以用卫语句去实现。卫语句就是当某个条件满足时直接退出函数或者抛出异常
Replace Conditional with Polymorphism:对于类似switch语句的条件表达式,可以使用多态去实现
Introduce Null Object:对于需要大量判断对象是否为空的情形,可以引入空对象表示空应用,这样就免去了判断为空情形
Introduce Assertion:引入断言,可以帮助测试

简化函数调用
Rename Method:当函数命名不规范时,大胆的将其重新命名
Add Parameter:当某个函数需要从调用端获取更多信息时,可以增加一个参数
Remove Parameter:当函数的某个参数不再使用时,可以将其移除
Separate Query from Modifier:当某个函数即返回对象状态又修改对象状态时,可以考虑将它们分割成两个函数,一个负责查询,一个负责修改
Parameterize Method:若干个函数做类似的工作,仅因为使用了不同的值。可以建立单一函数,以参数表达不同的值
Replace Parameter with Explicit Methods:和上述相反,当某个函数内的行为完全取决于参数值而采取不同的反应,可以将他们拆离开来,建立多个独立函数
Preserve Whole Object:当你的函数有多个参数取自同一个对象时,将这些参数用对象取代作为参数传给函数
Replace Parameter with Methods:当外部将某个函数的结果作为参数传递给另一个函数时,可以将另一个函数的这个参数去除,然后再另一个函数内部调用某个函数
Introduce Parameter Object:某些参数总是很自然的同时出现时,可以用一个对象取代他们
Remove Setting Method:当类的某个数据成员仅在对象创建之初赋值时,去掉该值域的所有设值函数
Hide Method:对于类中没有被外界使用到的函数,将其修改为private
Replace Constructor with Factory Method:当你在创建对象的而同时想完成除创建外的其他工作时,用工厂函数取代构造函数
Encapsulate Downcast:当你需要Downcast时,将其封装到一个函数中统一管理,Downcast极有可能出错
Replace Error Code with Exception:当某个函数返回一个特定的错误码时,用异常代替它
Replace Exception with Test:当调用者可以预先检测出调用函数锁发生的异常时,先检查,然后再调用函数,不用再try/catch了

处理概括关系
Pull Up Field:当每个子类都拥有相同数据成员时,将这个成员移至父类中
Pull Up Method:当每个子类都拥有相同函数成员时,将这个成员移至父类中
Pull Up Constructor Body:当每个子类的构造函数中都包含很多相同代码时,可以将这些代码移到父类的构造函数中
Push Down Method:当父类中某些代码仅与某个子类相关时,可以将这个函数移到这个子类中
Push Down Field:当父类中的某个数据成员仅与某个子类相关时,可以将这个成员移至子类中
Extract Subclass:当类中的某些特性仅被某些实例使用时,可以考虑建立一个子类,将这些特性移至子类中
Extract Superclass:当多个类中有相似特性时,可以建立一个公共父类,将这些特性移至父类中去,然后这些类从此父类派生
Extract Interface:当若干客户使用class接口中的同一子集或者另个class的接口有相同部分时,将这些相同子集提炼到同一个接口中
Collapse Hierarchy:当子类和父类之间差别很小时,可以考虑将他们合并
Form Template Method:当若干子类的某个函数所作操作仅有少许不同,可以在父类中用Template Method抽象一个函数实现相同部分,不同部分用多态在子类中实现
Replace Inheritance with Delegation:当某个子类仅使用使用父类中很少的功能时,可以在子类中保存父类的一个指针,将所需的功能委托给父类完成,然后断开两个类之间的继承关系
Replace Delegation with Inheritance:当你为了一个类能使用另一个类的功能而编写了大量的委托函数时,可以考虑让这个类从另一个类派生,从而取消委托

大型重构
Tease Apart inheritance:当某个继承链同时承担两项责任时,可以采用桥接模式建立两个继承体系,并通过委托关系让其中一个可以调用另一个
Convert Procedural Design to Objects;对于已有的过程化代码,可以将数据记录转化为对象,将行为分开并移到对象之中
Separate Domain from Presentation:当UI类中包含了大量的逻辑代码时,建议将这些代码分离出来,封装到独立的类中
Extract Hierarchy:当你的某个类做了太多工作,并且一部分是以大量条件式完成时,可以建立一个继承体系,以一个子类表示一种特殊情况

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值