1. Self Encapsulate Field 自封装字段
为字段建立Setter/Getter,并且只以这些函数来访问字段
做法:
为待封装字段建立取值/设置函数
找出该字段的所有引用点,将他们全部改为调用取值/设值函数
将该字段声明为private
复查,确保找出所有引用点
编译测试
2. Replace Data Value with Object 以对象取代数据值
有一个数据项,需要与其他数据和行为一起使用才有意义
做法:
为待替换数值新建一个类,在其中声明一个final字段,其类型和源类中的待替换数值类型一样,然后在新类中加入这个字段的取值函数,再加上一个接受此字段为参数的构造函数
编译
将源类中的待替换数值字段的类型改为前面新建的类
修改源类中该字段的取值函数,令它调用新类的取值函数
如果源类构造函数中用到这个待替换字段(多半是赋值动作),我们就修改构造函数,令它改用新类的构造函数来对字段进行赋值动作
修改源类中待替换字段的设置函数,令它为新类创建一个实例
编译测试
现在,你有可能需要新类使用Change Value to Refrence
3. Change Value to Reference 将值对象改为引用对象
从一个类衍生出许多彼此相等的实例,比如同样表示2010-1-1的日期对象,希望将他们替换为同一个对象
做法:
使用 Replace Constructor with Factory Method
编译测试
决定由什么对象负责提供访问新对象的途径,可能是一个静态字典或一个注册表对象
决定这些引用对象应该预先创建好,或是应该动态创建
修改工厂函数,令它返回引用对象
4. Change Reference to Value
有一个引用对象,很小且不可变,而且不易管理。这里有必要澄清一下“不可变”的意思。如果以Money类表示钱的概念,其中有“币种”和“金额”两条信息,那么Money对象通常是一个不可变的值对象。这并非意味你的薪资不能改变,而是意味:如果要改变你的薪资,就需要使用另一个Money对象来取代现有的Money对象,而不是在现有的Money对象上修改。
做法:
检查重构目标是否为不可变对象,或是否可修改为不可变对象
建立equals() 和 hashCode()
编译测试
考虑是否可以删除工厂函数,并将构造函数声明为public
5. Replace Array with Object 以对象取代数组
有一个数组,其中的元素各自代表不同的东西。以对象替换数组,对于数组中的每个元素,以一个字段来表示
String [] row = new String[3];
row[0] = "Liverpool";
row[1] = "15";
Performance row = new Performance();
row.setName("Liverpool");
row.setWins("15");
做法:
新建一个类表示数组所拥有的信息,并在其中以一个 public 字段保存原先的数组
修改数组的所有用户,让他们改用新类的实例
编译测试
逐一为数组元素添加Setter/Getter函数。根据元素的用途,为这些访问函数命名。修改客户端代码,让他们通过访问函数取用数组内的元素,每次修改后,编译并测试。
当所有对数组的直接访问都转而调用访问函数后,将新类中保存该数组的字段声明为private
编译
对于数组内的每一个元素,在新类中创建一个类型相当的字段。修改该元素的访问函数,令它改用上述的新建字段。
每修改一个元素,编译并测试
数组的所有元素都有了相应字段之后,删除该数组
6. Duplicate Observed Data 复制”被监视数据“
一些领域数据置身于GUI控件中,而领域函数需要访问这些数据,将该数据复制到一个领域对象中,建立一个Observer模式,用以同步领域对象和GUI对象内的重复数据。
该例比较复杂,请参考书内描述
7. Change Unidirectional Association to Bidirectional 将单向关联改为双向关联
两个类都需要使用对方特性,但其间只有一条单向连接。添加一个反向指针,并使修改函数能够同时更新两条连接
做法:
在被应用类中增加一个字段,用以保存反向指针
决定由哪个类——引用端还是被引用端——控制关联关系。如果是”一对多“关系,那么就由”拥有单一引用“的哪一方承担”控制者“角色。
在被控端建立一个辅助函数,其命名应该清楚指出它的有限用途
如果既有的修改函数在控制端,让它负责更新反向指针
如果既有的修改函数在被控端,就在控制端建立一个控制函数,并让既有的修改函数调用这个新建的控制函数。
8. Change Bidirectional Association to Unidirectional 将双向关联改为单向关联
去除不必要的关联
做法:
找出保存”你想去除的指针“的字段,检查它的每一个用户,判断是否可以去除该指针
如果客户使用了取值函数,先运用 Self Encapsulate Field 将待删除字段自我封装起来,然后使用 Substitute Algorithm 对付取值函数,令它不再使用该字段,然后编译测试。
如果客户并未使用取值函数,那就直接修改待删除字段的所有被引用点:改以其他途径获得该字段所保存的对象。每次修改后,编译并测试
如果已经没有热河函数使用待删除字段,移除所有对该字段的更新逻辑,然后移除该字段。
编译测试
9. Replace Magic Number with Symbolic Constant 以字面常量取代魔法数
创建一个常量,根据其意义为它命名,并将上述的字面数值替换为这个常量
10. Encapsulate Field 封装字段
类中存在一个public 字段,将它声明为private,并提供相应的访问函数。
11. Encapsulate Collection 封装集合
让这个函数该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数。
做法:
加入为集合添加/移除元素的函数
将保存集合的字段初始化为一个空集合
编译
找出集合设值函数的所有调用者。你可以修改那个设值函数,让它使用上述新建立的”添加/移除元素“函数;也可以直接修改调用端,改让他们调用上述新建立的”添加、移除元素“函数。
编译测试
找出所有”通过取值函数获得集合并修改其内容“的函数,逐一修改这些函数,让他们改用添加、移除函数。每次修改后,编译并测试
修改完上述所有”通过取值函数获得集合并修改集合内容“的函数后,修改取值函数自身,使它返回该集合的一个只读副本。使用Collection.unmodifiableXxxx()得到该集合的只读副本。
编译测试
找出取值函数的所有用户,从中找出应该存在于集合所属对象内的代码,运用Extract Method和Move Method将这些代码移到宿主对象去