1、自封装字段(Self En capsulate Field)
(1)症状:直接访问一个字段,但与字段之间耦合关系逐渐变得笨拙
(2)解决:为这个字段建立取值/设值函数,并且只以这些函数来访问字段
(3)间接访问变量的好处:子类可以通过覆写一个函数而改变取数据的途径
(4)直接访问变量的好处:代码比较容易阅读
- 为待封装字段建立取值/设值函数
- 找出该字段的所有引用点,将它们全部改为调用取值/设值函数
- 将该字段声明为private
- 复查,确保找出所有引用点
2、以对象取代数据值(Replace Data Value with Object)
(1)症状:有一个数据项需要与其他数据和行为一起使用才有意义
(2)解决:将数据项变为对象
- 为待替换数值新建一个类,在其中声明一个final字段,其类型和源类中的待替换数值类型一样,然后在新类中加入这个字段的取值函数,再加上一个接受此字段为参数的构造函数
- 编译
- 将源类中的待替换数值字段的类型改为前面新建的类
- 修改源类中该字段的取值函数,令它们调用新类的取值函数
- 如果源类构造函数中用到了这个待替换字段(多半是赋值动作),我们就修改构造函数,令它们改用新类的构造函数来对字段进行赋值
- 修改源类中待替换字段的设值函数,令它为新类创建一个实例
- 编译,测试
class Order...
private String _customer;
public Order(String customer){
_customer = customer;
}
public String getCustomer(){
return _customer;
}
public void setCustomer(String arg){
_customer = arg;
}
private static int numberOfOrdersFor(Collection orders, String customer){
int result = 0;
Iterator iter = orders.iterator();
while(iter.hashNext()){
Order each = (Order) iter.next();
if(each.getCustomer().equals(customer)) result++;
}
return result;
}
--------------------------------------------------------------------------
重构(以对象取代数据值)之后的代码
class Customer{
private final String _name;
public Customer(String name){
_name = name;
}
public String getName(){
return _name;
}
}
class Order...
private Customer _customer;
public Order(String customerName){
_customer = new Customer(customerName);
}
public string getCustomerName(){
return _customer.getName();
}
public void setCustomer(String customerName){
_customer = new Customer(customerName);
}
3、将值对象改为引用对象(Change Value to Reference)
(1)症状:从一个类衍生出许多彼此相等的实例,希望将它们替换成同一个对象
(2)解决:将这个值对象变成引用对象
(3)值对象常常用于保存少量不可修改的数据,如果希望加入一些可修改的数据,并确保对任何一个对象的修改都能影响到所有引用此一对象的地方,就需要将这个对象变成一个引用对象
class Customer{
private final String _name;
public Customer(String name){
_name = name;
}
public String getName(){
return _name;
}
}
//Customer类被下面Order类使用
class Order...
private Customer _customer;
public Order(String customerName){
_customer = new Customer(customerName);
}
public string getCustomerName(){
return _customer.getName();
}
public void setCustomer(String customerName){
_customer = new Customer(customerName);
}
//上述Customer对象还是值对象,每个Order对象还是拥有各自的Customer对象
//希望得到不同的Order对象可以共享同一个Customer对象,意味着:每个客户只对应一个Customer对象
//1、采用工厂方法代替构造函数,在Customer类中定义工厂函数
//2、把原本的构造函数的声明改为private
//3、在Order类中原本调用Customer构造函数的地方改为调用工厂函数
class Customer{
private final String _name;
private Customer(String name){
_name = name;
}
public String getName(){
return _name;
}
public static Customer create (String name){
return new Customer(name);
}
}
class Order...
private Customer _customer;
public Order(String customerName){
_customer = Customer.create(customerName);
}
public string getCustomerName(){
return _customer.getName();
}
public void setCustomer(String customerName){
_customer = Customer.create(customerName);
}
4、将引用对象改为值对象(Change Reference to Value)
(1)症状:有一个引用对象,很小且不可变,而且不易管理
(2)解决:将它变为一个值对象
(3)在分布式系统和并发系统中,不可变的值对象无需考虑同步问题
(4)不可变概念:以Money类为例,Money类表示钱,其中有“币种”和“金额”两条信息,Money对象是一个不可变的值对象并非意味着薪资不能改变,而是意味着:如果你要改变薪资,就需要使用另一个Money对象来取代现有的Money对象,而不是在现有的Money对象上修改
你和Money对象之间的关系可以改变,但Money对象自身不能改变
- 检查重构目标是否为不可变对象,或是否可修改为不可变对象
- 建立equals()和hashCode()
- 编译与测试
- 考虑是否可以删除工厂函数,并将构造函数声明为public
5、以对象取代数组(Replace Array with Object)
(1)症状:你有一个数组,其中元素各自代表不同的东西
(2)解决:以对象替换数组,对于数组中的每个元素,以一个字段来表示
(3)对象可以运用字段名称和函数名称来传达不同元素的含义
(4)使用对象可以将信息封装起来,并为它加上相关行为
(5)所有对数组的直接访问都转而调用对象中的函数,将对象中保存该数组的字段声明为private
String[] rows = new String[3];
row[0] = "Liverpool";
row[1] = "15";
String name = row[0];
int wins = Integer.parseInt(row[1]);
--------------------------------------------------------
重构(以对象取代数组)后的代码
class Performance...
private String _name;
public String getName(){
return _name;
}
public void setName(String arg){
_name = arg;
}
String _wins
public int getWins(){
return Integer.parseInt(_wins);
}
public void setwins(String arg){
_wins = arg;
}
6、复制“被监视数据”(Duplicate Observed Data)
(1)症状:有些领域数据置身于GUI控件中,而领域函数需要访问这些数据
(2)解决:将该数据复制到一个领域对象中,建立一个Observer模式,用以同步领域对象和GUI对象内的重复数据
(3)一个良好的系统应该将处理用户界