有时候你会认为某个对象应该是去全局唯一的,这就是引用(Reference)的概念。它代表当你在某个地点对他进行修改之后,那么所有共享他的对象都应该在再次访问他的时候得到相应的修改。而不会像值对象(Value)一样,不可修改。举个例子,你认识小明,我也认识小明,小明忽然把头发都踢了,这个时候你认识的小明和我认识的小明都是同一个人,都是光头,这个小明就是世界的唯一实例,然而,你有100块钱,我有50块钱,我把50块钱花到只剩20,你手里的100块钱并不会因为我的50块钱改变而改变,不会相应的修改,这就是值对象。放到C++代码中,对于引用的概念,我们可以参照上一篇的例子

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

为了让我的m_customer产生“值对象”的概念,我在每次进行set的时候都进行重新创建,这样可以保证我的对象是不可以进行修改的,也就确保了值对象的特性。总结来说,引用对象就像"客户",“账户”这样的东西,每个对象代表真实世界中的一个实物。你可以直接用“==”来比较两个对象是否相等,而值对象更像“日期”,“钱”这样的东西,你不需要去担心它副本的存在,也许系统中存在成百上千个100元钱这种对象,但这并不关系到你,当然如果你要做比较,你也可以重载“==”来做比较。

这也让我们在引用对象和值对象之间产生了两难。有时候,你会从一个简单的值对象开始,在其中保存少量不可修改的数据,而后,你可能希望给这个对象加上一些可修改的数据,并确保对任何一个对象的修改都能影响到所有引用到此对象的地方。这时候你就应该考虑将这个对象变成一个引用对象。

做法:

  • 使用Replace Constructor with Factory Method.
  • 编译,测试。
  • 决定由什么对象负责提供访问新对象的途径。可能是一个静态字典或者一个注册表对象,你也可以使用多个对象作为新对象的访问点。
  • 决定这些引用对象应该事先创建好,还是应该动态创建。如果这些引用对象是预先创建好的,而你必须从内存中将他们读取出来,那么就得确保他们在需要的时候被及时加载。
  • 修改工厂函数,令它返回引用对象。如果对象是预先创建好的,你就需要考虑万一有人索求一个其实并不存在的对象,要如何处理错误。你可能希望对工厂函数使用Rename Method,使其传达这样的信息,它返回的是一个既存对象。
  • 编译,测试。

例子:

class Customer
{
public:
Customer(const QString &name) :
m_name(name)
{
} QString name() const
{
return m_name;
}
private:
const QString m_name;
}; class Order
{
public:
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);
} QString customerName() const
{
return m_customer->name();
}
private:
Customer *m_customer;
};

我们引用了Replace Data Value with Object的例子,上一节我们也说过重构并没有因此结束,接下来就是要进行这一步骤。我们有Customer类,可以看到他正在被Order类所使用,并且对待的原则是『值对象』,我们可以看到客户端代码如下

static int numberOfOrderFor(const QList<Order> &orders, const QString &customerName)
{
int result = ; foreach (Order order, orders)
{
if (order.customerName() == customerName)
{
result++;
}
} return result;
}

到目前位置,Customer还是属于值对象,这就造成了就算多个订单属于一个客户,但这个一个客户对于每个Order来说都是自己的。我们希望改变这种现状,希望让客户Customer变成世界唯一。也就是说要让这些订单共享同一个Customer对象,也就意味着每一个客户只该对应一个Customer.

首先我们使用Replace Constructor with Factory Method,以此来控制Customer对象的创建过程,这在以后也是非常重要的,接下来我们在Customer类中定义这个工厂函数。

class Customer
{
public:
static Customer *create(const QString &name)
{
return new Customer(name);
}
};

然后把原本调用构造函数的地方都改为调用工厂函数。

class Order
{
public:
Order(const QString &customerName)
{
m_customer = Customer::create(customerName);
}
};

然后我们再把原来Customer的构造函数声明为private.

class Customer
{
private:
Customer(const QString &customerName)
{
m_name = name;
}
};

现在我们需要决定如何来访问这些唯一的Customer对象,作者比较喜欢通过Order中的一个字段来访问它,但本例没有这样一个明显的字段可以用来访问Customer对象,在这种情况下,通常我们需要创建一个注册表对象来保存所有的Customer对象,以此作为访问点。为了简化我们的例子,我们把这个注册表保存在Customer类的static中,让Customer来作为访问点。

static QHash<QString, Customer *> hashTables;

做完这个决定之后我们需要做下一个重要决定----是应该在请求时才创建还是应该预先创建好。这里我们选择后者,在应用程序的启动代码中,我们先把需要使用的Customer对象加载妥当,这些对象可能来自数据库,也可能来自文件。为求简单起见,我们在代码中明确生成这些对象,反正之后我们总是可以使用Subsititute Algorithm来改变他们的创建方式。

class Customer
{
public:
static void loadCustomers()
{
new Customer("Leo")->store();
new Customer("Tom")->store();
new Customer("Marry")->store();
}
private:
void store()
{
hashTables.insert(this.name(), this);
}
};

现在我们需要修改工厂函数,让它返回预先创建好的Customer对象。

static Customer *create(const QString &name)
{
return hashTables.value(name);
}

由于create总是返回既有的Customer对象,所以我们应该使用Rename Method来修改这个工厂函数的名称,以便强调这一点。

class Customer
{
public:
static Customer *getNamed(const QString &name)
{
return hashTables.value(name);
}
};

『重构--改善既有代码的设计』读书笔记----Change Value to Reference的更多相关文章

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

    如果你有一个引用对象,很小且不可改变,而且不易管理,你就需要考虑将他改为一个值对象.在Change Value to Reference我们说过,要在引用对象和值对象之间做选择,有时候并不容易,有了重 ...

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

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

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

    当你在一个类中使用字段的时候,发现这个字段必须要和其他数据或者行为一起使用才有意义.你就应该考虑把这个数据项改成对象.在开发初期,我们对于新类中的字段往往会采取简单的基本类型形式来保存,但随着我们开发 ...

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

    在面向对象中,对于类这个概念我们应该有一个清晰的责任认识,就是每个类应该只有一个变化点,每个类的变化应该只受到单一的因素,即每个类应该只有一个明确的责任.当然了,说时容易做时难,很多人可能都会和我一样 ...

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. 南阳acm奇偶数分离

    这道题的特殊要求是要先先限定了测试数据的组数,所以多加一条循环语句.下面是已通过的代码: #include<stdio.h>   main() {         int n,m,i,j; ...

  2. Java学习之路(一) —— Java命名规范

    Package 的命名 Package 的名字应该都是由一个小写单词组成. Class 的命名 Class 的名字必须由大写字母开头而其他字母都小写的单词组成 Class 变量的命名 变量的名字必须用 ...

  3. Eclipse非常有用的快捷键

    Eclipse有强大的编辑功能, 工欲善其事,必先利其器, 掌握Eclipse快捷键,可以大大提高工作效率. 小坦克我花了一整天时间, 精选了一些常用的快捷键操作,并且精心录制了动画, 让你一看就会. ...

  4. C#封装加密算法(MD5、SHA、HMAC、DES、RSA)的一个类

    using System; using System.IO; using System.Security.Cryptography; using System.Text; namespace Wen. ...

  5. git push 报错

    git push报错误: Git push error: RPC failed; result=56, HTTP code = 200 fatal: The remote end hung up un ...

  6. [置顶] 文件io(一)--unix环境高级编程读书笔记

    unix-like(后面以linux为例)系统中的文件操作只需要五个函数就足够了,open.close.read.write以及lseek.这些操作被称为不带缓存的io,这里有必要说一下带缓存和不带缓 ...

  7. MachineKey

    我是在收到用户发来的这个错误信息的截图后才认识到什么是MachineKey的. 有关MachineKey的概念.MachineKey的生成以及web.config文件里的配置,网上一搜一大堆,为了方便 ...

  8. hdu2047.java递推题

    阿牛的EOF牛肉串 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total S ...

  9. Android开发之显示通知

    Toast类可以用来显示消息给用户,虽然它很方便,但是有不能持久.它只是在屏幕上显示几秒后就自动消失掉了.对于重要的信息或消息,要使用更加持久的方法.这种情形下,就应当使用通知,即使用Notifica ...

  10. 对RecycleView的多种item布局的封装

    本文是借鉴bingoogolapple写得BGAAdapter-Android而产生的,对此表示感谢. 效果 1.Adapter的使用 1.继承BaseAdapter 这里是我的adapter pub ...