你有一笔数据项(data item),需要额外的数据和行为。

将这笔数据项变成一个对象。

class Order...
private string customer;

==>

class Order...
private Customer _customer;

class Customer...
private string _name;

动机

一开始你可能会用一个字符串来表示[电话号码]概念,但是随后你就会发现,电话号码需要[格式化]、[抽取区号]之类的特殊行为。当这些臭味开始出现,你就应该将数据值(data value)变成对象(object)。

作法

1. 为[待替换数值]新建一个class,在其中声明一个final值域,其型别和source class中的[待替换数值]型别一样。然后在新class中加入这个值域的取值函数(getter),再加上一个[接受此值域为参数]的构造函数。

2. 编译。

3. 将source class中的[待替换数值值域]的型别改为上述的新建class。

4. 修改source class中此一值域的取值函数(getter),令它调用新建class的取值函数。

5. 如果source class构造函数中提及这个[待替换值域](多半是赋值动作),我们就修改构造函数,令它改用新class的构造函数来对值域进行赋值动作。

6. 修改source class中[待替换值域]的设值函数(setter),令它为新class创建一个实体。

7. 编译,测试。

8. 现在,你有可能需要对新class使用Change Value to Reference(179)。

下面有一个代表[定单]的Order class,其中以一个字符串记录定单客户。现在,我希望改为以一个对象来表示客户信息,这样我就有充裕的弹性保存客户地址、信用等级等等信息,也得以安置这些信息的操作行为。Order class最初如下:

class Order...
public Order(String customer) {
_customer = cusomer;
}

public String getCustomer() {
return _customer;
}

public void setCustomer(String arg) {
_customer = arg;
}
private String _customer;

Order class的客户代码可能像下面这样:

private static int numberOfOrdersFor(Collection orders, String customer) {
int result = 0;
Iterator iter = orders.iterator();
while(iter.hasNext()) {
Order each = (Order)iter.next();
if(each.getCustomer().equals(customer)) result ++;
}
return result;

}

首先,我要新建一个Customer class来表示[客户]概念。然后在这个class中建立一个final值域,用以保存一个字符串,这是Order class目前所使用的。我将这个新值域命名为_name,因为这个字符串的用途就是记录客户名称。此外我还要为这个字符串加上取值函数(getter)和构造函数(constructor)。

class Customer {
public Customer(String name) {
_name = name;
}

public String getName() {
return _name;
}
private final String _name;
}

现在,我要将Order中的_customer值域的型别修改为Customer;并修改所有引用此一值域的函数,让它们恰当地改而引用Customer实体。其中取值函数和构造函数的修改都很简单;至于设值函数(setter),我让它创建一份Customer实体。
class Order...
public Order(String customer) {
_customer = new Customer(customer);
}

public String getCustomer() {
return _customer.getName();
}

public void setCustomer(String arg) {
_customer = new Customer(arg);
}
private Customer _customer;

设值函数需要创建一份Customer实体,这是因为以前的字符串是个实值对象(value object),所以现在的Customer对象也应该是个实值对象。这也就意味每个Order对象都包含自己的一个Customer对象。注意这样一条规则:实值对象应该是不可修改内容的--这便可以避免一些讨厌的[别名](aliasing)错误。日后或许我会想让Customer对象成为引用对象(reference object),但那是另一项重构手法的责任。现在我可以编译并测试了。

public String getCustomerName() {
return _customer.getName();
}

至于构造函数和设值函数,我就不必修改其签名(signature)了,但参数名称得改:
public Order(String customerName) {
_customer = new Customer(customerName);
}
public void setCustomer(String customerName) {
_customer = new Customer(customerName);
}

本次 重构到此为止。但是,这个案例和其他很多案例一样,还需要一个后续步骤。如果想在Customer中加入信用等级、地址之类的其他信息,现在还做不到,因为目前的Customer还是被作为实值对象(value object)来对待,每个Order对象都拥有自己的Customer对象。为了给Customer class加上信用等级、地址之类的属性,我必须运用Change Value to Reference(179),这么一来属于同一客户的所有Order对象就可以共享同一个Customer对象。马上你就可以看到这个例子。

重构改善既有代码设计--重构手法19:Replace Data Value with Object (以对象取代数据值)的更多相关文章

  1. 重构改善既有代码设计--重构手法08:Replace Method with Method Object (以函数对象取代函数)

    你有一个大型函数,其中对局部变量的使用,使你无法釆用 Extract Method. 将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的值域(field) 然后你可以在同一个对象中将这个大型 ...

  2. 重构改善既有代码设计--重构手法04:Replace Temp with Query (以查询取代临时变量)

    所谓的以查询取代临时变量:就是当你的程序以一个临时变量保存某一个表达式的运算效果.将这个表达式提炼到一个独立函数中.将这个临时变量的所有引用点替换为对新函数的调用.此后,新函数就可以被其他函数调用. ...

  3. 重构改善既有代码设计--重构手法02:Inline Method (内联函数)& 03: Inline Temp(内联临时变量)

    Inline Method (内联函数) 一个函数调用的本体与名称同样清楚易懂.在函数调用点插入函数体,然后移除该函数. int GetRating() { return MoreThanfiveLa ...

  4. 重构改善既有代码设计--重构手法01:Extract Method (提炼函数)

    背景: 你有一段代码可以被组织在一起并独立出来.将这段代码放进一个独立函数,并让函数名称解释该函数的用途. void PrintOwing(double amount) { PrintBanner() ...

  5. 重构改善既有代码设计--重构手法16:Introduce Foreign Method (引入外加函数)&& 重构手法17:Introduce Local Extension (引入本地扩展)

    重构手法16:Introduce Foreign Method (引入外加函数)你需要为提供服务的类增加一个函数,但你无法修改这个类.在客户类中建立一个函数,并以第一参数形式传入一个服务类实例. 动机 ...

  6. 重构改善既有代码设计--重构手法11:Move Field (搬移字段)

    你的程序中,某个字段被其所驻类之外的另一个类更多的用到.在目标类建立一个新字段,修改源字段的所有用户,令它们改用新字段.        动机:在类之间移动状态和行为,是重构过程中必不可少的措施.随着系 ...

  7. 重构改善既有代码设计--重构手法07:Remove Assignments to Parameters (移除对参数的赋值)

    代码对一个 参数赋值.以一个临时变量取代该参数的位置.     int Discount(int inputVal, int quantity, int yearTodate) { if (input ...

  8. 重构改善既有代码设计--重构手法05:Introduce Explaining Variable (引入解释性变量)

      发现:你有一个复杂的表达式. 解决:将该复杂的表达式(或其中的部分)的结果放进一个临时变量,并以此变量名称来解释表达式用途. //重构前 if((platform.toUpperCase().in ...

  9. 重构改善既有代码设计--重构手法13:Inline Class (将类内联化)

    某个类没有做太多事情.将这个类的所有特性搬移到另一个类中,然后移除原类. 动机:Inline Class (将类内联化)正好于Extract Class (提炼类)相反.如果一个类不再承担足够责任.不 ...

随机推荐

  1. mininet实验 连接floodlight控制器

    参考博客一 参考博客二 事先准备-floodlight安装 Java安装方法及环境变量配置 执行ifconfig命令获取floodlight所在服务器的IP地址. 1.启动floodlight jav ...

  2. 软工1816 · Alpha冲刺(5/10)

    团队信息 队名:爸爸饿了 组长博客:here 作业博客:here 组员情况 组员1(组长):王彬 过去两天完成了哪些任务 后端代码复审 福大各个食堂的菜品口味量化.属性标记 组织前后端线下协作 接下来 ...

  3. lintcode-420-报数

    420-报数 报数指的是,按照其中的整数的顺序进行报数,然后得到下一个数.如下所示: 1, 11, 21, 1211, 111221, ... 1 读作 "one 1" -> ...

  4. git找回当前目录下误删的所有文件

    git checkout . 参考:http://opentechschool.github.io/social-coding/extras/delete-restore.html

  5. Delphi下使用指针的简单总结

    由于最近公司太忙,好久没有更新我的BLOG了.原来想着写写关于HOOK驱动的文章,可是最后想想好久已经没有做驱动的东西了,怕写出来有错误,于是作罢.开发游戏也有一段时间了,发现使用DELPHI来开发网 ...

  6. HDU4472_Count

    题目的意思简单,给你n个点,每个点(除根节点)都有且只有一个父节点.现在问你这些节点关系组成的图有多少种情况满足同一层节点上每个点的后继节点数量相等. 很简单,对于n节点,直接枚举n-1的的所有约数情 ...

  7. 【uoj#22】[UR #1]外星人 组合数学+dp

    题目描述 给你一个长度为 $n$ 的序列 $\{a_i\}$ 和一个数 $x$ ,对于任意一个 $1\sim n$ 的排列 $\{p_i\}$ ,从 $1$ 到 $n$ 依次执行 $x=x\ \tex ...

  8. CSS预处理语言-less 的使用

    Less 是一门 CSS 预处理语言,它扩展了 CSS 语言,增加了变量.Mixin.函数等特性,使 CSS 更易维护和扩展. Less 可以运行在 Node 或浏览器端. Less的编译处理 作为一 ...

  9. 【明哥报错簿】之 mybatis异常invalid comparison: java.util.Date and java.lang.String

    背景:数据库为postgresql,表字段属性为timestamp格式 原因是mybatis 3.3.0中对于时间参数进行比较时的一个bug. 如果拿传入的时间类型参数与空字符串''进行对比判断则会引 ...

  10. POJ P3254 Corn fields 【状压dp】

    Corn Fields Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 16909 Accepted: 8939 Descript ...