当你在一个类中使用字段的时候,发现这个字段必须要和其他数据或者行为一起使用才有意义。你就应该考虑把这个数据项改成对象。在开发初期,我们对于新类中的字段往往会采取简单的基本类型形式来保存,但随着我们开发进度的增加,这些简单的数据项就不再那么简单了。比如一开始你会使用一个字符串来表示一串电话号码,但是随后你会发现,这个电话号码已经变的不再纯粹,它可能还需要“格式化”,“抽取取号”等特殊行为。一开始你可能会不以为意,觉得这个数据项就这么一两个,不会对你造成影响。但重复代码(Duplicate Code)和依恋情结(Feature Envy)这两个坏味道会很快就散发出来。什么样的行为就应该放在什么样的类中去,当这些坏味道出现的时候你就应该将数据值改为对象了。

  • 做法:
  • 为待替换数值新建一个类,并在这个新类中新建一个const字段(Java final)并保持其类型和源类中你需要替换的数值类型一样,然后在新类中加入一个这个字段的取值函数(get),并加上一个接受此字段为参数的构造函数。
  • 编译。
  • 将源类中待替换数值的类型改为你前面新建的类类型。
  • 修改源类中关于这个字段的取值函数,令他调用新类的取值函数。
  • 如果源类构造函数中用到这个待替换字段(多半是赋值动作)你就应该修改构造函数,让它变为用新类的构造函数来给这个字段赋值。
  • 修改源类中待替换字段的设值函数(set)令他为新类创建一个实例。
  • 编译,测试。
  • 现在,你可能还会对新类使用Change Value to Reference。

例子:

class Order
{
public:
Order(const QString &customer) :
m_customer = customer; QString customer()
{
return m_customer;
} void setCustomer(const QString &customer)
{
m_customer = customer;
}
private:
QString m_customer;
};

我们可以看到Order这个类中,用一个字符串来代表订单客户,但随着开发进度,很可能到了后期你需要为这个客户增加客户地址,信用等级等,这个时候你就不应该用字符串来表示客户,取而代之你应该使用对象。一开始使用Order这个类的客户端代码可能是这样

static int numberOfOrdersFor(QList<Order> orders, QString customer)
{
int result = ; foreach (Order order, orders)
{
if (order.customer() == customer)
{
result++;
}
} return result;
}

首先我们新建Customer类用来表示我们需要替换的新类,然后在这个新类中增加一个const字段用来表示源类中替换字段,这里表示客户姓名name这个概念,然后为这个字段增加取值函数和构造函数。

class Customer
{
public:
Customer(const QString &name) :
m_name(name)
{
} QString name() const
{
return m_name;
}
private:
const QString m_name;
};

接下来我们就需要修改源类了,首先我们将替换字段的类型从QString替换为我们所定义的类型Customer。然后修改所有引用该字段的函数,让他们改而去引用Customer这个对象,其中取值函数和构造函数的修改比较简单,设值函数我们让他重新创建一个新的Customer对象来跟之前的语义(字符串)达成一致形成值对象而非引用对象的概念

class Order
{
public:
Order(const QString &customer)
{
m_customer = new Customer(customer);
} QString customer() const
{
return m_customer.name();
} void setCustomer(const QString &value)
{
delete m_customer;
m_customer = NULL; m_customer = new Customer(value);
}
private:
Customer m_customer;
};

这也就意味着每个Order有属于自己的Customer,注意这样一条规则:值对象是不可以修改内容的。这便可以让你避免一些别名问题。如果你日后想让Customer成为引用对象(reference object)那就是另外一个重构手法了,现在我们进行编译并且测试。

同时需要观察Order类中m_customer字段的操作函数,并作出一些修改使它更好的反应修改后的形式。对于取值函数你可以使用Rename Method让它更清晰的表示自己,因为他返回的是消费者名称,因为此时的消费者已经是一个确确实实的类了。

QString customerName() const
{
return m_customer.name();
}

至于构造函数和设值函数,就不需要修改签名了,但你可以改变他们的参数名称。

Order(const QString &customerName)
{
m_customer = new Customer(customerName);
} void setCustomer(const QString &customerName)
{
delete m_customer;
m_customer = NULL; m_customer = new Customer(customerName);
}

当然,后续的重构也许会添加接受现有的Customer对象作为参数的构造函数和设值函数。

本次重构到此为止,但这个案例和其他案例一样,只是一个中间步骤,还需要后期处理。因为从上文我们可以看到目前我们对待Customer这个类是采用值的概念来对待。如果要给这些Customer增加地址和信用等级我们做不到,因为多个Order存储的Customer不是共同引用同一个对象,所以我们必须使用Change Value to Reference,这样一来,同一个客户的所有Order就可以共享一个Customer对象达到我们所要的效果了。

『重构--改善既有代码的设计』读书笔记----Replace Data Value with Object的更多相关文章

  1. 『重构--改善既有代码的设计』读书笔记----Replace Method with Method Object

    有时候,当你遇到一个大型函数,里面的临时变量和参数多的让你觉得根本无法进行Extract Method.重构中也大力的推荐短小函数的好处,它所带来的解释性,复用性让你收益无穷.但如果你遇到上种情况,你 ...

  2. 『重构--改善既有代码的设计』读书笔记----Replace Array with Object

    如果你有一个数组,其中的元素各自代表不同东西,比如你有一个 QList<QString> strList; 其中strList[0]代表选手姓名,strList[1]代表选手家庭住址,很显 ...

  3. 『重构--改善既有代码的设计』读书笔记----Replace Temp with Query

    Replace Temp with Query,顾名思义,表示你用查询来替换临时变量本身,临时变量对于函数来说是只有当前函数可见的,如果你在同类的别的地方要用到这个变量你就必须重新写表达式来获取这个变 ...

  4. 『重构--改善既有代码的设计』读书笔记----Change Value to Reference

    有时候你会认为某个对象应该是去全局唯一的,这就是引用(Reference)的概念.它代表当你在某个地点对他进行修改之后,那么所有共享他的对象都应该在再次访问他的时候得到相应的修改.而不会像值对象(Va ...

  5. 『重构--改善既有代码的设计』读书笔记----Extract Method

    在编程中,比较忌讳的一件事情就是长函数.因为长函数代表了你这段代码不能很好的复用以及内部可能出现很多别的地方的重复代码,而且这段长函数内部的处理逻辑你也不能很好的看清楚.因此,今天重构第一个手法就是处 ...

  6. 『重构--改善既有代码的设计』读书笔记----Introduce Explaning Variable

    有时候你会遇到一系列复杂的表达式连续运算的时候,这个时候你可能根本招架不住如此长或者是如此复杂的长函数.这个时候你可以通过引用临时变量来储存他们的结果,将这些长函数的结果分成一个个临时变量来让函数清晰 ...

  7. 『重构--改善既有代码的设计』读书笔记---Duplicate Observed Data

    当MVC出现的时候,极大的推动了Model与View分离的潮流.然而对于一些已存在的老系统或者没有维护好的系统,你都会看到当前存在大把的巨大类----将Model,View,Controller都写在 ...

  8. 『重构--改善既有代码的设计』读书笔记----Self Encapsulate Field

    如果你直接访问一个字段,你就会和这个字段直接的耦合关系变得笨拙.也就是说当这个字段权限更改,或者名称更改之后你的客户端代码都需要做相应的改变,此时你可以为这个字段建立设值和取值函数并且只以这些函数来访 ...

  9. 『重构--改善既有代码的设计』读书笔记----Move Method

    明确函数所在类的位置是很重要的.这样可以避免你的类与别的类有太多耦合.也会让你的类的内聚性变得更加牢固,让你的整个系统变得更加整洁.简单来说,如果在你的程序中,某个类的函数在使用的过程中,更多的是在和 ...

随机推荐

  1. Bzoj 2818: Gcd 莫比乌斯,分块,欧拉函数,线性筛

    2818: Gcd Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 3241  Solved: 1437[Submit][Status][Discuss ...

  2. nyoj 67 三角形面积【三角形面积公式】

    三角形面积 时间限制:3000 ms  |  内存限制:65535 KB 难度:2   描述 给你三个点,表示一个三角形的三个顶点,现你的任务是求出该三角形的面积   输入 每行是一组测试数据,有6个 ...

  3. 【Lucene3.6.2入门系列】第14节_SolrJ操作索引和搜索文档以及整合中文分词

    package com.jadyer.solrj; import java.util.ArrayList; import java.util.List; import org.apache.solr. ...

  4. iScroll使用

    新公司比较喜欢用iScroll,而我对此一无所知,特此调研iScroll用法,写在这里方便查看 IScroll是移动页面上被使用的一款仿系统滚动插件. myScroll = new IScroll(& ...

  5. Intellij IDEA 导入Eclipse或MyEclipse的Web项目(旧版 转载)

    Intellij IDEA 导入Eclipse或MyEclipse的Web项目 博客分类: Intellig IDEA Intellij IDEAEclipseWeb  Intellij IDEA 导 ...

  6. ecshop判断搜索引擎是否为蜘蛛

    <?php /** * 判断是否为搜索引擎蜘蛛 * * @access public * @return string */ function is_spider($record = true) ...

  7. 【设计模式 - 17】之中介者模式(Mediator)

    1      模式简介 中介者模式的定义: 用一个中介者对象封装一系列的对象交互,中介者使各对象不需要显式地相互作用,从而使耦合松散,而且可以独立地改变它们之间的交互. 中介者模式中的组成部分: 1. ...

  8. win10亮度不能调节的解决方法

    如果大家遇到Win10屏幕亮度调不了,该如何解决呢?以下是电脑百事网小编亲测比较有用的几种方法,希望能够帮助大家. 修改注册表解决Win10屏幕亮度无法调节 1.首先使用 Win+R 组合快捷键打开W ...

  9. 海思android4.4 SDK编译Latin输入法

    原来的HiSTBAndroidV500R001C01SPC020\device\hisilicon\bigfish\packages\apps\HiLatinIME\Android.mk内容例如以下: ...

  10. 【iOS】iOS之Button segue弹出popOver消除(dismiss)问题

    如图.由于程序须要,点击Button Ctrl+Dragging加入了一个UITableViewController,当然其余的Controller也能够,这样我们在方法 -(void)prepare ...