您现在的位置是:首页 >技术交流 >关于重构的原则和思想总结网站首页技术交流
关于重构的原则和思想总结
重构定义:在不改变软件外部可观察行为的前提下,对软件内部结构的调整行为,提高代码的可读性,降低修改成本
1.一些感悟
- 重构的目的实际上,就是为了使代码更容易理解和修改,什么是外部可观察行为,其实面向客户和其他程序员,功能和代码调用无改变,都不知道已经有东西发生了改变
- 开发过程中,两顶帽子,一定不可以同时戴上,重构和新功能开发
- 消除重复代码,是重构中经常做的事情,优秀设计的根本在于,将重复代码,归于一处,将不变抽离,将可变使用java的多态性,进行设计演变兼容
- 重构使软件更容易理解,重构应该随时随地的进行,不应该为了重构而重构,你之所以重构,是因为想做的什么事情,重构可以帮你做的更好
- 重构并不是修改错误代码,请谨记。重构时你总会发现某些代码并不正确。你绝对相信自己的判断,因此想马上把它们改正过来。啊,顶住诱惑,别那么做。重构时你的目标之一就是保持代码的功能完全不变,既不多也不少。对于那些需要修改的东西,列个清单把它们记录下来(通常我在计算器旁边放一张索引卡),需要添加或修改 的测试用例(test cases)、需要进行的其他重构、需要撰写的文档、需要画的图…… 都暂时记在卡上。这样就不会忘掉这些需要完成的工作。千万别让这些工作打乱你 手上的工作。重构完成之后,再去做这些事情不迟。
2.一些重构的方法
2.1 重新组织你的函数
1)Extract Method(提炼函数)
Extract Method是我最常用的重构手法之一。当我看见一个过长的函数或者一段需要注释才能让人理解用途的代码,我就会将这段代码放进一个独立函数中。
一个函数多长才算合适?在我看来,长度不是问题,关键在于函数名称和函数本体之间的语义距离。
使用此方法过程中,会发生临时变量的问题,那么此时根据实际情况(改变值?改变了对象?)来使用不同的手段,重构优化, Replace Temp with Query (以查询取代临时) or Remove Assignments to Parameters(移除对参数的赋值)
2)Inline Method(将函数内联化)
3)Replace Temp with Query(以查询取代临时变量)
4) Introduce Explaining Variable
5)Split Temporary Variable(剖解临时变量)
如果临时变量承担多个责任,它就应该被替换(剖 解)为多个临时变量,每个变量只承担一个责任。同一个临时变量承担两件不同的 事情,会令代码阅读者糊涂。
6)Remove Assignments to Parameters(移除对参数的赋值动作)
7)Replace Method with Method Object(以函数对象取代函数)
8)Substitute Algorithm(替换你的算法)
2.2 在对象之间搬移特性
1)Move Method(搬移函数)
2)Move Field
3)Extract class 某个class做了应该由两个classes做的事。
4)Inline Class(将类内联化)你的某个class没有做太多事情(没有承担足够责任)。
5)Hide Delegate(隐藏「委托关系」),很像代理,坏处显而易见,就是被代理的对象有新增行为时,代理类需要修改
6)Remove Middle Man(移除中间人),和上面的隐藏委托关系,相反
7)Introduce Foreign Method(引入外加函数),引入static函数
你所使用的server class需要一个额外函数,但你无法修改这个class。
8)Introduce Local Extension(引入本地扩展),继承或者装饰
2.3 重新组织数据
1) Self Encapsulate Field(自我封装),其实就是get、set
2) Replace Data Value with Object (用对象替换数据),将基本数据类型,封装为对象
3) Change Value to Reference (将实值对象改为引用对象)
将这个value object (实值对象)变成一个reference object (引用对象)
4) Replace Array with Object(用对象替换array)
5)Duplicate Observed Data(复制「被监视数据」),观察者模式
应该将处理用户界面(UI)和处理业务逻辑(business logic)的代码分开
6) Change Unidirectional Association to Bidirectional(将单向关联改为双向)
7) Change Bidirectional Association to Unidirectional(将双向关联改为单向)
8) Replace Magic Number with Symbolic Constant(以符号常量/字面常量取代魔法数)
9) Encapsulate Collection(封装群集)
10) Replace Type Code with Class(以类取代型别码)
switch语句中,根据类型,返回不同的对象,则可以将switch去除,通过模板设计模式,进行去除
11) Replace Subclass with Fields(以值域取代子类)
2.4 简化条件表达式
1)Decompose Conditional(分解条件式)
将if、else,进行函数抽离
2)Consolidate Conditional Expression(合并条件式)
将多个判断返回相同的代码,抽离为一种情况函数
3)Consolidate Duplicate Conditional Fragments(合并重复的条件片段)
将重复的条件判断,合并为一个函数
4)Remove Control Flag(移除控制标记)
以break 语句或return 的语句取代控制标记。
5)Replace Nested Conditional with Guard Clauses(以卫语句取代嵌套条件式)Hi
其实就是把多层嵌套语句,拆解为一个一个的简单判断函数,if else多层嵌套,那么拆解为多个单的if,然后再利用合并重复的条件判断,将其合并
6)Replace Conditional with Polymorphism(以多态取代条件式)
你手上有个条件式,它根据对象型别的不同而选择不同的行为。将这个条件式的每个分支放进一个subclass 内的覆写函数中,然后将原始函数声明为抽象函数(abstract method)。
7)Introduce Null Object(引入Null 对象)
2.5 简化函数调用
1)Rename method
2)Add parameter
3)Rmove parameter
4)Separate query from modifier(将修改和查询分离)
不要在一个函数中,既对一个对象修改,又返回,尽量让其只有一个功能
5)Preserve whole object(保持整个对象)
6)Introduce parameter object
7)Replace parameter with methods
8)Hide method
9)Replace construction with factory method
工厂模式,不应该用type来区别,这时应该想到反射+泛型的做法,因为这样的话,后续新增type,不必要再维护create函数
10)Encapsulate downcase(封装向下转型)
11)Replace error code with exception(以异常取代error code)
2.6 处理继承关系
1)Pull Up Field(值域上移)
2)Pull Up Method(函数上移)
其实两者都是指 subclass 与superclass 的函数与值域的相同提炼
3)Pull Up Constructor Body(构造函数本体上移)
其实就是在子类中,应该尽量使用super,而非自己完全重新创造
4)Push Down Method(函数下移)、Push Down Field(值域下移)
这两种情况出现的概率很小,父类中声明的方法或者值域,仅适用于某个子类,那么此时应该move
5)Extract Superclass(提炼超类)
6)Extract Interface(提炼接口)
7)Collapse Hierarchy(折叠继承关系),子类和父类并无多大区别,那么合并他们
8)Form Template Method(塑造模板函数)
两个子类中有相同的行为,但是他们又不是完全相同,那么此时可以抽离相同的部分,在父类中,形成模板函数
9)Replace Inheritance with Delegation(以委托取代继承)
某个subclass 只使用superclass 接口中的一部分,或是根本不需要继承而来的数据。
在subclass 中新建一个值域用以保存superclass ;调整subclass 函数,令它改而委托superclass ;然后去掉两者之间的继承关系。
10)Replace Delegation with Inheritance(以继承取代委托)
这个其实挺不建议的, 可以通过 Remove Middle Man 让客户端自己调用受托函数,也可以使用Extract Superclass 将两个classes 接口相同的部分提炼到superclass 中, 然后让两个classes 都继承这个新的superclass ;你还可以以类似手法使用Extract Interface。
3.总结
永远不要忘记「两顶帽子」重构与开发是两顶帽子,要时常交换进行,不可以同时戴上。