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

自古以来,面向对象关于字段访问就存在两个派系,其中一个派系认为,如果在该字段所在的类中,你就可以自由访问他,不需要通过间接的函数来获得。另外一派认为,即使在这个类中你也应该只使用设值和取值函数来间接访问。

当然了,间接访问可以带来的额外好处就是你可以通过子类来覆写函数做到改变获取数据的途径。他还可以让你支持灵活的数据管理方式,比如延迟初始化等。直接访问变量的好处也有,就是可以让我们阅读代码的人给我们更加明确清晰的思路,不要追函数追到一般才发现这原来只是一个简单的取值函数,从而影响我们的判断。

在面对这种选择的时候,我支持作者的观点,我在该字段本类中我会优先去使用直接访问的形式,直到这种直接访问的形式给我带来麻烦的时候,此时我才会去选择改变才用间接访问的方式,这也是重构带给我们的魅力,他可以让我们自由改变我们需要的方式。

如果你想访问超类中的一个字段,却又想在子类中对这个变量的访问改为一个计算后的值(或者针对那种简单的类型码判断,基类和子类返回不一样的类型码)这就是使用Self Encapsulate Filed的时候,字段自我封装只是第一步,完成自我封装之后,你可以在子类中根据自己的需要随意覆写取值/设值函数。

做法:

  • 为待封装字段建立设值/取值函数。
  • 找出该字段的所有引用点,将他们全部改为设值/取值函数。如果引用点要读取字段值,就将他改为调用取值函数。如果引用点要给字段,就将他改为设值函数。当然这里也有一个小技巧帮你可以更快更全的找到所有引用点,就是你通过对字段改名,借助编译器帮你找到所有这些引用点。
  • 将该字段声明为private.
  • 复查,确保找到所有引用点。
  • 编译,测试。

例子:

class IntRange
{
public:
IntRange(int low, int high) :
m_low(low),
m_high(high)
{
} bool includes(int arg)
{
return arg >= m_low && arg <= m_high;
} void grow(int factor)
{
m_high = m_high * factor;
}
private:
int m_low;
int m_high;
}

为了封装m_low和m_high我们定义取值/设值函数,并使用它们。

class IntRange
{
public:
IntRange(int low, int high) :
m_low(low),
m_high(high)
{
} bool includes(int arg)
{
return arg >= low() && arg <= high();
} void grow(int factor)
{
m_high = m_high * factor;
} virtual int low() const
{
return m_low;
} virtual int high() const
{
return m_high;
}
private:
int m_low;
int m_high;
};

同时这里有一点需要注意的就是你的取值函数和设值函数如果是在构造函数中进行调用的你就需要注意,现在C++默认在构造函数中是不会呈现多态性的,也就是说如果你在基类的构造函数中放了一个虚函数,当你创建子类的时候,当进入到基类的构造函数的时候并不会去调用子类的虚函数,依然还是去调用基类的虚函数。

同时还需要注意的就是默认一般来说,设值函数被认同是在对象创建之后才使用的,所以初始化过程中的行为可能与设值函数行为不同,这种情况下,你要么就是在构造函数中直接访问字段,要么就是单独创建另一个初始化函数。

IntRange(int low, int high) :
m_low(low),
m_high(high)
{
initialize(low, high);
} void initialize(int low, int high)
{
m_low = low;
m_high = high;
}

当然,现在你也许看不到什么价值,但如果你一旦拥有了子类,上述体现的价值就出来了。

class CappedRange : public IntRange
{
public:
CappedRange(int low, int high, int cap) :
IntRange(low, high),
m_cap(cap)
{
} virtual int cap() const
{
return m_cap;
} virtual int high() const
{
return qMin(IntRange::high(), cap());
} private:
int m_cap;
};

现在你可以在high()中进行覆写,从而加上范围上限的考虑而不必修改IntRange的任何行为。

『重构--改善既有代码的设计』读书笔记----Self Encapsulate Field的更多相关文章

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

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

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

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

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

    在类与类之间搬移状态和行为,是重构过程中必不可少的步骤.很有可能在你现在觉得正常的类,等你到了下个礼拜你就会觉得不合适.或者你在下个礼拜创建了一个新的类并且你需要讲现在类的部分字段和行为移动到这个新类 ...

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

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

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

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

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

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

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

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

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

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

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

    重构可以把复杂的东西分解成一个个简单的小块.但有时候,你必须壮士断腕删掉整个算法,用简单的算法来取代,如果你发现做一件事情可以有更清晰的方式,那你完全有理由用更清晰的方式来解决问题.如果你开始使用程序 ...

随机推荐

  1. 用Delphi7开发Web Service程序 转

        转:http://rosehacker.blog.51cto.com/2528968/450160 用Delphi7开发Web Service程序,并把服务程序放在IIS Web服务器上提供给 ...

  2. apache php gzip压缩输出的实现方法

    一.gzip介绍 gzip是GNU zip的缩写,它是一个GNU自由软件的文件压缩程序,也经常用来表示gzip这种文件格式.软件的作者是Jean-loup Gailly和Mark Adler.1992 ...

  3. IIS7 上传 下载文件大小限制的设置

    IIS7 上传 下载文件大小限制的设置 本文来自IISBOY网 一.找到修改大小的配置文件和配置节点 本文来自IISBOY网 打开你系统盘(我是C盘),找到 C:\Windows\System32\i ...

  4. Android学习笔记(一)Android应用程序的组成部分

    Android应用程序由松散耦合的组件组成,并使用应用程序Manifest绑定到一起:应用程序Manifest描述了每一组件和它们之间的交互方式,还用于指定应用程序元数据.其硬件和平台要求.外部库以及 ...

  5. 一个cocos2d程序的完整人生(从环境到代码全过程)

    今天我的打砖块小游戏Beta0.1终于完成了,比较开心,写一下这个程序从出生到长大的全过程把. 这是个博客集合帖,具体的操作细节我都在其它博文中有详细说明,下面会给出链接   首先,我想我还是要介绍一 ...

  6. Howto Setup yum repositories to update or install package from ISO CDROM Image

    Step # 1: Mount an ISO file Type the following command (replace iso file name with the actual iso fi ...

  7. mysql创建数据库(指定编码)

    如下脚本创建数据库yourdbname,并制定默认的字符集是utf8. CREATE DATABASE IF NOT EXISTS yourdbname DEFAULT CHARSET utf8 CO ...

  8. UINavigationBar-使用总结

    多视图应用程序中,我们常常使用到自定义UINavigationBar来完成导航条的设置.   1.获取导航条   UINavigationBar *navBar = self.navigationCo ...

  9. Python程序的执行原理(转载)

    Python程序的执行原理 2013-09-17 10:35 佚名 tech.uc  1. 过程概述 Python先把代码(.py文件)编译成字节码,交给字节码虚拟机,然后虚拟机一条一条执行字节码指令 ...

  10. springframework hibernate Transaction not successfully started

    先贴出错误:org.springframework.transaction.TransactionSystemException: Could not commit Hibernate transac ...