Summary:
有一个数据项,需要与其他数据和行为一起使用才有意义。将数据项变成对象。
Motivation:
开发初期,我们以简单的数据项表示简单的情况。但是随着开发的进行,你可能会发现,这些简单数据项不再那么简单了。比如说,一开始你可能会用一个字符串来表示“电话号码”概念,但是随后就会发现,电话号码需要“格式化”、“抽取区号”之类的特殊行为。如果这样的数据有一两个,还可以把相关函数放进数据项所属的对象里;但是Duplicate Code坏味道和Feature Envy坏味道很快就会从代码中散发出来。当这些坏味道开始出现,就应该将数据值变成对象。
Mechanics:
1.为带替换数据新建一个类,在其中声明一个final字段,其类型和源类中的带替换数值类型一样。然后在新类中加入这个字段的取值函数,再加上一个接受此字段为参数的构造函数。
2.编译
3. 将源类中的带替换数值字段的类型改为前面新建的类。
4. 修改源类中该字段的取值函数,令它调用新类的取值函数。
5. 如果源类构造函数中用到这个待替换字段,我们就修改构造函数,令它改用新类的构造函数来对字段进行赋值动作。
6.修改源类中待替换字段的设值函数,令它为新类创建一个实例。
7.编译,测试
8. 现在,可能需要对新类使用Change Value to Reference.
范例
下面有一个代表“订单”的Order类,其中以一个字符串记录订单客户。现在,我们希望改用一个对象来表示客户信息,这样就有充裕的弹性保存客户地址、信用等级等信息,也得以安置这些信息的操作行为。Order类最初如下:
public class Order
{
private String customer;
public Order( String customer )
{
this.customer = customer;
}
public String getCustomer()
{
return customer;
}
public void setCustomer( String arg )
{
customer = arg;
}
}
使用Order类的代码可能像下面这样:
private static int numberOfOrdersFor( Collection orders, String customer )
{
int result = 0;
Iterator<Order> iter = orders.iterator();
while( iter.hasNext() )
{
Order each = iter.next();
if( each.getCustomer().equals( customer ) )
{
result++;
}
}
return result;
}
首先,新建一个Customer类表示“客户”概念。然后在这个类中建立一个final字段,用以保存一个字符串,这是Order类目前所使用的。将这个新字段命名为name,为这个字符串加上取值函数和构造函数.
public class Customer
{
private final String name;
public Customer( String name )
{
this.name = name;
}
public String getName()
{
return name;
}
}
现在,将Order中的customer字段的类型修改为Customer,并修改所有引用该字段的函数,让它们恰当地改而引用Customer对象。其中取值函数和构造函数的修改都很简单。至于设值函数,让它创建一个customer实例。
public class Order
{
private Customer customer;
public Order( String customer )
{
this.customer = new Customer( customer );
}
public String getCustomer()
{
return customer.getName();
}
public void setCustomer( String arg )
{
customer = new Customer( arg );
}
}
设值函数需要创建一个Customer实例,这是因为以前的字符串是个值对象,所以现在的Customer对象也应该是个值对象。这也就意味着每个对象都包含自己的一个Customer对象。注意这样一条规则:值对象应该是不可修改内容的--这便可避免一些讨厌的别名问题。
我们需要观察Order类中的customer字段的操作函数,并作出一些修改,使它更好地反映出修改后的新形势。对于取值函数,可以使用Rename Method改变名称,让它更清晰地表示,它所返回的是消费者名称,而不是个Customer对象。
public void setCustomerName( String arg )
{
customer = new Customer( arg );
}
至于构造函数和设值函数,就不必修改其签名了,但参数名称得改:
public Order( String customerName )
{
customer = new Customer( customerName );
}
public void setCustomerName( String customerName )
{
customer = new Customer( customerName );
}
后继的其他重构也许会添加“接受现有Customer对象作为参数”的构造函数和设值函数。