Refactoring 笔记

Refactoring 笔记

2008-11-21   version 1.1

 

1       定义

不改变代码外在行为的前提下,对代码进行修改,用以改善程序的内部结构。

2       Refactoring的原则

1)        小步前进,每次更改一点点,在发生错误时能进行回归;大量的更改叫做重写;

2)        重构后测试失败一定要进行回归,而不是就地调试;

3)        勤于测试

a)         在代码更改前添加欠缺的测试

b)        测试的实用主义原则:放过那些肯定不会出错的函数,如getset函数;

4)        抵制诱惑:重构不能改变代码外在表现,不要试着去改变原代码中的错误;将外在行为的更改与重构分开!——Kent Beck

5)        重构时尽量不要添加、更改测试;

3       重构的历史与其他

重构是一种早期广泛应用于smalltalk的方法,在OO发展的早期(80年代),重构技术便已经开始产生并发展;关于重构的第一份正式文本,是一篇博士论文;Martin Fowlerrefactoring》一书中文版于2003年翻译出版;

Refactoring Browser是一个用于smalltalk的重构工具;VAssistX也有一些重构功能;

4       重构的常见问题

l         更改接口:首先让旧接口调用新接口,通过再删除新接口;

l         重构与性能:更多的封装、委托,看起来设计优良的系统跑起来慢;但大量的经验表明,必须经过测试才能确定热点,任何分析、臆断都有可能是错的;“重构时不要过多考虑性能,优化时再考虑;”

5       好的面向对象代码的特点

小类与小函数,避免巨型类与巨型函数;

责任明确的类;


 

6       代码的“臭味”

首先说明:并非有任何一些臭味的代码都是不好的代码,如某些设计模式就可能有过多的依赖的问题;对此问题的原则是:隔离变化,将某个因素可能引发的变更尽量放到一起;

6.1    Duplicated code

解决办法:使用Extract Method;出现在不同类中的重复代码,使用Extract Class将其独立到一个类中;

6.2    Long Method

“如果需要用注释来说明一段代码,那就把这段代码提炼到一个函数中,同时起一个足够说明问题的名字。”

Extract Method分解函数;

6.3    Large class

类拥有过多数据成员和/或成员函数;

6.4    Long parameter list

Replace parameter with methodintroduce parameter object等方法;

6.5    Divergent change(发散式变化)

某个class经常因为不同的原因在不同的方向上变化;

理想上每个类应该只因为一个原因而变化;

Extract class将一个方向上的变化提炼出来;

6.6    Shotgun surgery(霰弹式修改)

遇到一个变化需要在多个class做小修改;

Move methodMove Field将需要修改的因素集中到一个class中;

6.7    Feature Envy

Class的某个函数过于依赖其他一个class

Move Method将其移到那个class中,或先Extract method将依赖部分提取出,再move新函数;

6.8    Data clumps

总是一起出现的数据,如几个相同参数出现在一系列函数中,或好几个class中;

Extract class将它们放到一个独立对象中;

6.9    Primitive obsession

程序员不愿使用小class,而偏向使用基本数据类型;

6.10       Switch statements

OO程序的一个“最明显”特点:少用switch语句;多态是一个替换方案;

减少switch的方法:

Extract method先将switch语句提出;再move method将它移到需要多态性的类中;使用replace type code with subclassreplace type code with state/strategy,实现类层次;replace conditional with polymorphism替换条件判断;

一个简单的方法:replace parameter with explicit methods

6.11       Parallel inheritance hierarchies

平行继承体系,每当为一个class增加一个subclass,就需要为另一个class相应增加一个subclass

解决之道:让一个继承体系refer to另一个继承体系,再使用move method/move field解决之;

6.12       Lazy class

几乎无用的class

6.13       Speculative generality

对未来变化过分的预测——“总有一天会用到”;

6.14       Temporary field

某些数据成员仅为特定场景、method而设置;

Extract class将该数据成员及相关函数提炼出来,这可能会是一个method object

6.15       Message chains

对象相互委托,需要通过一个调用链(message chain)才能获得最终的数据;如a.getB().getC().getD().getE();

解决之道:

1,如上例更改成a.getB().getE(),但此时B可能会变成一个Middle man;

2,更好的处理,将E的使用提取出来,再推入chain中;


6.16       Middle man

Class过多接口都委托给其他class完成;

Remove middle man直接与实责对象交互,或replace delegation with inheritancemiddle man变成实责classsubclass

6.17       Inappropriate intimacy

两个class过于密切,相互需要知道对方的大量private信息;

Move methodmove fieldextract class等;

6.18       Alternative classes with different interfaces

两个函数、类使用不同的定义但做了同样的事;

6.19       Incomplete library class

类库不够用;

Introduce foreign methodintroduce local extension

6.20       Data class

仅拥有数据成员及getset这些成员的函数的class

注意类是数据及相关操作的集合;

getset函数的用户的其他某些行为尽量移植到data class中;

6.21       Refused bequest

Subclass不需要superclass的所有数据和函数;

如果不需要继承接口,尝试replace inheritance with delegation

或将子类不需要的部分提炼成一个子类的兄弟类;

6.22       Comments

comments被当成除臭剂时,重构代码;


 

7       Refactoring目录

7.1    函数组织

7.1.1    Extract method

l         将一段代码提取出来作为一个新的函数;

l         重点考虑临时变量、新函数的参数与返回值;

7.1.2    Inline method

l         将函数调用替换成函数体;

l         确保函数没有多态性;

7.1.3    Inline temp

l         使用表达式替换临时变量;

7.1.4    Replace temp with query

l         将临时变量的计算式提炼成函数(即query),再用它替换掉临时变量;

l         用于只被赋值一次的临时变量;如果赋值超过一次,考虑split temporary variable

l         将变量声明为constfinal,编译;再进行提取、测试;

7.1.5    Introduce explaining variable

l         将复杂表达式的结果保存到一个const temp中,以temp名解释表达式的含义;

7.1.6    Split temporary variable

l         Temp被赋值超过一次,此时为每次赋值都创造一个独立的temp

7.1.7    Remove assignment to parameters

l         如果函数一个参数被赋值,则使用一个temp取代该参数;

l         参数只用于表示“被传递进来的东西”,而不被同时作为函数的一个temp,能提高代码清晰度;

7.1.8    Replace method with method object

l         使用单独对象替换大型函数;

l         该方法比较复杂,To be continued

7.1.9    Substitute algorithm

l         将函数体替换成一个更清晰的算法;

7.2    对象间移动

7.2.1    Move method

l         Class的一个函数与另一个class过于密切;

l         考虑该函数用到的其他成员是否也应该被移动;

l         检查函数是否没有多态性;

l         首先尝试将原函数改成delegation,编译测试,再考虑删除原函数;

7.2.2    Move field

l         首先将public field隐藏,编译测试;再执行移动;

7.2.3    Extract class

l         从原有的class中分离出一个新的class

l         小步操作,多次测试;

7.2.4    Inline class

l         删除职责太少的class

l         首先在目标类中实现要删除的classpublic接口;改成委托,测试;最后执行删除;

7.2.5    Hide delegate

l         Client直接调用了server的委托类,将其改成间接调用;

7.2.6    Remove middle man

l         某个class做了过多的简单委托动作,此时让client直接调用delegate

7.2.7    Introduce foreign method

l         一个server class不能提供满足需要的函数,此时在client class中定义一个需要的函数;

l         该函数不应使用client class的任何特性;server classobject应该是函数的第一个参数;

7.2.8    Introduce local extension

l         Server class不能提供需要的功能,此时引入一个新的class,该classserver classsubclasswrapper

7.3    Organizing data

7.3.1    Self encapsulate field

l         field建立set/get函数;

l         暂时将field改名,编译查出所有的引用点;

7.3.2    Replace data value with object

l         数据项需要别的数据和行为,此时建立一个新的class表达该数据项及其相关部分;

7.3.3    Change value to reference

l         Reference object,代表真实世界中的实物;value object,完全使用其包含的数据值来定义,如日期;

l         To be continued

7.3.4    Change reference to value

l         Reference对象小而不可变时,变成value对象以简化管理;

7.3.5    Replace array with object

7.3.6    Duplicate observed data

7.3.7    Change unidirectional association to bidirectional

7.3.8    Change bidirectional association to unidirectional

7.3.9    Replace magic number with symbolic constant

7.3.10   Encapsulate field

7.3.11   Encapsulate collection

7.3.12   Replace record with data class

l         使用一个哑数据class代替record

l         这个class当前只有getset函数,进一步的重构这种情况会改变的;

7.3.13   Replace type code with class

7.3.14   Replace type code with subclass

7.3.15   Replace type code with state/strategy

7.3.16   Replace subclass with fields

7.4    Simplifying conditional expressions

7.4.1    Decompose conditional

7.4.2    Consolidate conditional expression

7.4.3    Consolidate duplicate conditional fragments

7.4.4    Remove control flag

7.4.5    Replace nested conditional with guard clauses

7.4.6    Replace conditional with polymorphism

7.4.7    Introduce null object

7.4.8    Introduce assertion

7.5    Making method calls simpler

7.5.1    Rename method

7.5.2    Add parameter

7.5.3    Remove parameter

7.5.4    Separate query from modifier

7.5.5    Parameterize method

7.5.6    Replace parameter with explicit methods

7.5.7    Preserve whole object

l         一个函数需要用到object的数个成员,那么就使用该object作为函数参数,而不是独立的那几个成员;

7.5.8    Replace parameter with method

7.5.9    Introduce parameter object

7.5.10   Remove setting method

7.5.11   Hide method

7.5.12   Replace construction with factory method

7.5.13   Encapsulate downcast

7.5.14   Replace error code with exception

l         Exception用于将错误错误从一般处理中分离出来;这是一种集中处理错误的方式;

l         还是没有告诉我们为什么要使用exception

7.5.15   Replace exception with test

l         Exception仅应被用于处理那些意料外的错误,而不是“条件检查”的替代品;

l         使用正常的条件检查来避免异常处理;

 

7.6    Dealing with generalization

7.6.1    Pull up field

7.6.2    Pull up method

7.6.3    Pull up constructor body

7.6.4    Push down method

7.6.5    Push down field

7.6.6    Extract subclass

7.6.7    Extract superclass

7.6.8    Extract interface

7.6.9    Collapse hierarchy

7.6.10   Form template method

7.6.11   Replace inheritance with delegation

7.6.12   Replace delegation with inheritance

7.7    Big refactoring

7.7.1    Tease apart inheritance

分析类体系承担的责任,将不同的责任拆分到不同的类结构中,如使用bridge模式在不同的类体系中建立关联;

7.7.2    Convert procedural design to objects

record转化成带数据及其存取的class;将过程化代码提取到某个class中;分解大函数;

7.7.3    Separate domain from presentation

将业务逻辑从展现中拆分出来;

7.7.4    Extract hierarchy

 

 

 

你可能感兴趣的:(object,Class,smalltalk,hierarchy,inheritance,Refactoring)