有时候你会认为某个对象应该是去全局唯一的,这就是引用(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. 关于MySQL的分区(partion)

    1 CREATE TABLE part_tab ( c1 int default NULL, c2 ) default NULL, c3 date default NULL ) engine=myis ...

  2. OpenCV学习(一)

    环境:OpenCV 2.4.4 VS2010 第一个Demo,显示一张图片 #include "opencv2/highgui/highgui.hpp" int main( int ...

  3. Yii框架常见问题汇总

    然用过Yii做了一个小项目了,但是过程中间解决的问题没有随手记下来,导致新项目开始后,以前碰到的问题还得在查一遍,干脆就记下来,以便不时之需. 有新的会随时更新. 1.如何显示ActiveRecord ...

  4. 解决oracle_4031错误的方法

    原因分析: 大量的硬解析出现,产生大量小的free chunk突然出现大的sql 1.临时方法 alter system flush shared_pool; 2.共享SQL alter system ...

  5. 关于 vim每次w时都提示 “E509: 无法创建备份文件 (请加 ! 强制执行)”

    今天网上git下vim的配置后v只要执行:wq就会出现这样的话,找了点资料说是没有.vimbk文件夹,我加了也没用,后来发现在root权限下就不会出现这样的问题,然后我就把当前文件夹下所以的有关vim ...

  6. return 和 exit

    此篇文不会阐述具体的原理,而是只记录实际应用如何避免一些问题 在<C语言程序设计-现代方法>第9.5章节中有这样一段说明, return语句和exit函数之间的差异是:不管哪个函数调用ex ...

  7. apache启动目录(禁止目录)与设置默认入口文件的方法

    设置默认入口文件的方法: 打开apache的conf目录,找到httpd.conf文件,打开这个文件,搜索dir_module,找到以下截图修改位置进行修改,注意重启apache服务器,修改位置才会生 ...

  8. GC overhead limit exceeded解决

    java.lang.OutOfMemoryError: GC overhead limit exceeded解决   一.异常如下:Exception in thread "main&quo ...

  9. VC++深入详解-第一章学习心得(二)

    前面介绍到了注册窗口,接下来进行窗口的创建,利用CreateWindow函数,结构如下 CreateWindowExW( DWORD dwExStyle, LPCWSTR lpClassName, / ...

  10. [Machine Learning (Andrew NG courses)]II. Linear Regression with One Variable