Martin Fowler于2003年出版的,有些观点有些陈旧了,可取其精华,去其糟粕。
前言:
作者介绍了重构的基本概念,以及回答了what when who的问题。
可能需要重构的方面:
1、Duplicate Code(重复代码)
2、Long Method(长函数)
3、Large Class(大类)
4、Long ParameterList(过长参数)
5、Divergent Change(函数太集中)、Shotgun Surgey(函数太发散):更改某个需求,不要更改太多的地方。我觉得这个有点难以掌握,得根据经验来做了。
6、Feature Envy(一个类用到了太多另个类的函数)
7、Data Clumps(数据泥团)
8、Primitive Obsession(基本类型偏执狂):喜欢用大堆基本类型来表达,推荐尝试用小型类
9、Switch Statements(Switch语句):switch语句导致重复和复杂,运用多态替换switch
10、Refused Bequest(被拒绝的馈赠):superClass尽量最小话,也就是说subClass共享superClass所有的资源,可额外制造自己的工具类。
11、Data Class(纯粹的数据类):可能作者当时的年代没有POJO这一说法,所以作者认为一个没有“行为”的数据类是不对的,其实这点不是确定的,各有所长,也是目前争议的地方。
12、Temporary Field(令人迷惑的临时变量):尽量让临时变量变得可读。
构筑测试体系:
作者推荐在编码阶段就开始构造单元测试,可为每个类写main方法来测试,这个我目前觉得还点苛刻了。
重构的方法:
1、分解大函数,把大函数里面的部分操作分解成独立的小函数。有利于复用和可读。
2、合并小函数。这都是很博弈的过程了,视情形而行。
3、关于临时变量,作者推荐一个临时变量只被赋值一次,这个我觉得有点苛刻。
3、将复杂表达式的结果放入临时变量。
4、将临时变量转为成员变量从而分解大函数。这个我也不赞成,我觉得还是“最近原则”较好,成员变量会暂用内存,且很容易使类混乱,特别是有继承关系的时候,或者尽量保持成员变量声明为private。
5、移动函数。如果Class1中某个函数与Class2有太多的合作而形成高耦合,就需要搬动这个函数了。
6、移动成员变量。 如果Class1中某个值域被Class2过多的应用,就需要考虑搬动这个值域了。
7、提炼类:一个class应该是一个清楚的抽象,具有明确的责任。对于过大的类需要进行分解。
8、Inline Class:与方法7相反,是合并一些类。
9、增加中间层来取代类与类的关系。典型的委托模式,对外永远为一个代理接口,客户不用关心内部关系是如何变化的。
10、Remove Middle Man:删除过多的简单中间层。可以看到,很难定义什么程度的隐藏才是最合适的,项目不同时期,这个度也是不断变化的,这个就得靠自己把握了。
11、直接使用值域或间接使用值域。所谓直接使用就是在类中直接使用成员变量,间接使用是使用成员变量的取值/赋值方法,即getXX()/setXX()方法,作者推荐这个最好根据团队习惯来定。
12、当行为过于庞大后,就需要分离数据成为无行为的类。POJO就是典型。
13、使用工厂或不使用工厂。这个又是个博弈的过程,使用工厂的好处是对象可以复用,坏处是需要考虑同步问题,提高复杂度。
14、以对象替代数组。
15、尽量隐藏类中数据结构的细节。对外只提供操作的方法。
16、Replace record with data class/Replace type code wiht class
17、以值域取代子类。
18、引入NULL对象。把无效值变为无效类。
19、使用断言?
20、更改函数名。我觉得最有效的重构方式。
21、查询函数和修改函数分离。类似setXXX和getXXX。
22、移除setXXX或使用final。变量只在初始化时赋值。
23、隐藏函数。封装即隐藏,对外只提供接口,对内封装细节。
24、Pull Up Field/Method,把subClass中的重复代码提升到superClass。
25、Pull down Field/Method。
26、以委托取代继承。某个subClass只使用接口中的一部分,或是根本不需要使用父类的数据。
现实问题:
1、程序员不知道重构。
2、你很快已经不在职位上了。
3、重构是额外工作,老板付钱给你是为了编写新功能。
4、重构可能破坏现有程序。
重构就是一个博弈的过程,一个寻找平衡点的过程,空间和时间的平衡点,清晰和工作量的平衡点,共性和个性的平衡点...如果没有看到实际的情况,谁也无法知道具体该怎么做,但务必保持“简约而不简单的”优良作风。