『重构--改善既有代码的设计』读书笔记----Change Value to Reference
有时候你会认为某个对象应该是去全局唯一的,这就是引用(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的更多相关文章
- 『重构--改善既有代码的设计』读书笔记----Change Reference to Value
如果你有一个引用对象,很小且不可改变,而且不易管理,你就需要考虑将他改为一个值对象.在Change Value to Reference我们说过,要在引用对象和值对象之间做选择,有时候并不容易,有了重 ...
- 『重构--改善既有代码的设计』读书笔记----Extract Method
在编程中,比较忌讳的一件事情就是长函数.因为长函数代表了你这段代码不能很好的复用以及内部可能出现很多别的地方的重复代码,而且这段长函数内部的处理逻辑你也不能很好的看清楚.因此,今天重构第一个手法就是处 ...
- 『重构--改善既有代码的设计』读书笔记----Replace Data Value with Object
当你在一个类中使用字段的时候,发现这个字段必须要和其他数据或者行为一起使用才有意义.你就应该考虑把这个数据项改成对象.在开发初期,我们对于新类中的字段往往会采取简单的基本类型形式来保存,但随着我们开发 ...
- 『重构--改善既有代码的设计』读书笔记----Extract Class
在面向对象中,对于类这个概念我们应该有一个清晰的责任认识,就是每个类应该只有一个变化点,每个类的变化应该只受到单一的因素,即每个类应该只有一个明确的责任.当然了,说时容易做时难,很多人可能都会和我一样 ...
- 『重构--改善既有代码的设计』读书笔记----Replace Method with Method Object
有时候,当你遇到一个大型函数,里面的临时变量和参数多的让你觉得根本无法进行Extract Method.重构中也大力的推荐短小函数的好处,它所带来的解释性,复用性让你收益无穷.但如果你遇到上种情况,你 ...
- 『重构--改善既有代码的设计』读书笔记---Duplicate Observed Data
当MVC出现的时候,极大的推动了Model与View分离的潮流.然而对于一些已存在的老系统或者没有维护好的系统,你都会看到当前存在大把的巨大类----将Model,View,Controller都写在 ...
- 『重构--改善既有代码的设计』读书笔记----Replace Array with Object
如果你有一个数组,其中的元素各自代表不同东西,比如你有一个 QList<QString> strList; 其中strList[0]代表选手姓名,strList[1]代表选手家庭住址,很显 ...
- 『重构--改善既有代码的设计』读书笔记----Self Encapsulate Field
如果你直接访问一个字段,你就会和这个字段直接的耦合关系变得笨拙.也就是说当这个字段权限更改,或者名称更改之后你的客户端代码都需要做相应的改变,此时你可以为这个字段建立设值和取值函数并且只以这些函数来访 ...
- 『重构--改善既有代码的设计』读书笔记----Move Method
明确函数所在类的位置是很重要的.这样可以避免你的类与别的类有太多耦合.也会让你的类的内聚性变得更加牢固,让你的整个系统变得更加整洁.简单来说,如果在你的程序中,某个类的函数在使用的过程中,更多的是在和 ...
随机推荐
- 【转】腾讯OCR—自动识别技术,探寻文字真实的容颜
文字,一种信息记录的图像符号,千年来承载了太多的人类文明印记.OCR,一种自动解读这种图像符号的技术,一直以来都备受关注.尤其在信息时代的今天,数字图像纷繁复杂,如何便捷高效的获取其中的文字信息,更有 ...
- DBA日常工作职责 - 我对DBA的七点建议
作者:eygle |English [转载时请标明出处和作者信息]|[恩墨学院 OCM培训传DBA成功之道]链接:http://www.eygle.com/archives/2009/02/sugge ...
- SourceTree的基本使用
1. SourceTree是什么 拥有可视化界面的项目版本控制软件,适用于git项目管理 window.mac可用 2. 获取项目代码 1. 点击克隆/新建 2. 在弹出框中输入项目地址,http或者 ...
- medoo数据库插入的问题
今天遇到疾行客同学提出的medoo insert方法不能插入数据库问题,我测试了一下 发现是可以的 而INSERT INTO "t_user" (username) VALUES ...
- git push 报错
git push报错误: Git push error: RPC failed; result=56, HTTP code = 200 fatal: The remote end hung up un ...
- 基于windows的ngnix基础使用
前言 今天组长一大早心血来潮的跟我说,我希望我们小组电脑做web站点的服务器集群,你搞一搞,就用ngnix吧. 君要臣死,臣不得不死.顺便写个文章做个笔记. 简介 Nginx 是一个高性能的HTTP和 ...
- BASH内置变量
BASH内置变量 ().BASH 作用:bash的完整路径.默认为/bin/bash ().BASH_ENV 作用:仅在非交互模式中适用.在执行shell脚本时,会先检查该变量是否指定了启动 脚本,若 ...
- careercup-栈与队列 3.2
3.2 请设计一个栈,除pop与push方法,还支持min方法,可返回栈元素中的最小值.push.pop和min三个方法的时间复杂度必须为O(1). 我们假设除了用一个栈s1来保存数据,还用另一个栈s ...
- ViewPager的用法
ViewPager 1.布局文件中的配置 ViewPager的路径 方法:在源码文件中输入ViewPager,按下alt+/,上面就会出来viewPager的包路径 viewPager的配置很简单,前 ...
- GDI 对象的释放与内存泄漏的问题研究
最近写了一个GDI 绘图的程序,过程中遇到一个奇怪的问题,就是 定时器定时一会GDI绘的图就消失了..后来经过分析,原来是 GDI对象数量过多 ,即GDI对象超过10000个 导致内存泄漏的问题.找到 ...