如果你直接访问一个字段,你就会和这个字段直接的耦合关系变得笨拙。也就是说当这个字段权限更改,或者名称更改之后你的客户端代码都需要做相应的改变,此时你可以为这个字段建立设值和取值函数并且只以这些函数来访问字段。
自古以来,面向对象关于字段访问就存在两个派系,其中一个派系认为,如果在该字段所在的类中,你就可以*访问他,不需要通过间接的函数来获得。另外一派认为,即使在这个类中你也应该只使用设值和取值函数来间接访问。
当然了,间接访问可以带来的额外好处就是你可以通过子类来覆写函数做到改变获取数据的途径。他还可以让你支持灵活的数据管理方式,比如延迟初始化等。直接访问变量的好处也有,就是可以让我们阅读代码的人给我们更加明确清晰的思路,不要追函数追到一般才发现这原来只是一个简单的取值函数,从而影响我们的判断。
在面对这种选择的时候,我支持作者的观点,我在该字段本类中我会优先去使用直接访问的形式,直到这种直接访问的形式给我带来麻烦的时候,此时我才会去选择改变才用间接访问的方式,这也是重构带给我们的魅力,他可以让我们*改变我们需要的方式。
如果你想访问超类中的一个字段,却又想在子类中对这个变量的访问改为一个计算后的值(或者针对那种简单的类型码判断,基类和子类返回不一样的类型码)这就是使用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的更多相关文章
-
『重构--改善既有代码的设计』读书笔记----Extract Method
在编程中,比较忌讳的一件事情就是长函数.因为长函数代表了你这段代码不能很好的复用以及内部可能出现很多别的地方的重复代码,而且这段长函数内部的处理逻辑你也不能很好的看清楚.因此,今天重构第一个手法就是处 ...
-
『重构--改善既有代码的设计』读书笔记---Duplicate Observed Data
当MVC出现的时候,极大的推动了Model与View分离的潮流.然而对于一些已存在的老系统或者没有维护好的系统,你都会看到当前存在大把的巨大类----将Model,View,Controller都写在 ...
-
『重构--改善既有代码的设计』读书笔记----Move Field
在类与类之间搬移状态和行为,是重构过程中必不可少的步骤.很有可能在你现在觉得正常的类,等你到了下个礼拜你就会觉得不合适.或者你在下个礼拜创建了一个新的类并且你需要讲现在类的部分字段和行为移动到这个新类 ...
-
『重构--改善既有代码的设计』读书笔记----Change Value to Reference
有时候你会认为某个对象应该是去全局唯一的,这就是引用(Reference)的概念.它代表当你在某个地点对他进行修改之后,那么所有共享他的对象都应该在再次访问他的时候得到相应的修改.而不会像值对象(Va ...
-
『重构--改善既有代码的设计』读书笔记----Replace Method with Method Object
有时候,当你遇到一个大型函数,里面的临时变量和参数多的让你觉得根本无法进行Extract Method.重构中也大力的推荐短小函数的好处,它所带来的解释性,复用性让你收益无穷.但如果你遇到上种情况,你 ...
-
『重构--改善既有代码的设计』读书笔记----Replace Array with Object
如果你有一个数组,其中的元素各自代表不同东西,比如你有一个 QList<QString> strList; 其中strList[0]代表选手姓名,strList[1]代表选手家庭住址,很显 ...
-
『重构--改善既有代码的设计』读书笔记----Move Method
明确函数所在类的位置是很重要的.这样可以避免你的类与别的类有太多耦合.也会让你的类的内聚性变得更加牢固,让你的整个系统变得更加整洁.简单来说,如果在你的程序中,某个类的函数在使用的过程中,更多的是在和 ...
-
『重构--改善既有代码的设计』读书笔记----Replace Data Value with Object
当你在一个类中使用字段的时候,发现这个字段必须要和其他数据或者行为一起使用才有意义.你就应该考虑把这个数据项改成对象.在开发初期,我们对于新类中的字段往往会采取简单的基本类型形式来保存,但随着我们开发 ...
-
『重构--改善既有代码的设计』读书笔记----Substitute Algorithm
重构可以把复杂的东西分解成一个个简单的小块.但有时候,你必须壮士断腕删掉整个算法,用简单的算法来取代,如果你发现做一件事情可以有更清晰的方式,那你完全有理由用更清晰的方式来解决问题.如果你开始使用程序 ...
随机推荐
-
javascript封装与多态的体现
封装是实现面向对象程序设计的第一步,封装就是将数据与函数等集合在一个个的单元中(我们称之为类).被封装的对象通常被称为抽象数据类型. 在传统的面向对象语言中有访问修饰符,如Private:只有类本身能 ...
-
[No000026]365种创业、办公、和生活成长的精华资源
只需要具备以下技能,人人都可以成为企业家:经得起失败的考验,思维活跃,新点子不断,能够脚踏实地把新点子转化为产品,并在这个过程中坚持不懈,百折不挠,即使跌倒了,也要及时从失败中学习,迅速投入到下一次冒 ...
-
C# 进制转换参考
//十进制转二进制 Console.WriteLine(Convert.ToString(69, 2)); //十进制转八进制 Console.WriteLine(Convert.ToString(6 ...
-
【转】COCOS2D-X之不断变化的数字效果Demo
我们在玩"天天爱消除"或"天天连萌"的时候,每玩一盘后会有一个游戏结果界面,上面有一个显示所得分数的效果.今天我们这个Demo就是要实现与之相近的效果. 一.我 ...
-
概率质量函数:怀孕周期的PMF
__author__ = 'dell' import surveyimport Pmfimport matplotlib.pyplot as pyplot table = survey.Pregnan ...
-
wpf ListBox 双击事件时在子项添加Style 确保选中
<ListBox ItemTemplate="{StaticResource CardPictureTemplate}" VirtualizingPanel.CacheLen ...
-
Head First设计模式之访问者模式
一.定义 定义:表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. 访问者模式适用于数据结构相对稳定的系统, 它把数据结构和作用于数据结构之上的操 ...
-
css3 背景色 实现边框渐变运动动画
css3 #body_id { animation: myfirst 10s ease-in-out -2s infinite alternate; /* Firefox: */ -moz-anima ...
-
【转】rem自适应布局
rem自适应原理 rem是根据html的font-size大小来变化,正是基于这个出发,我们可以在每一个设备下根据设备的宽度设置对应的html字号,从而实现了自适应布局.更多介绍请看这篇文章:rem是 ...
-
HashMap为什么存取效率那么高?
HashMap为什么存取效率那么高? 1.Hash 也叫散列.哈希. 主要用于信息安全领域中的算法,把长度不同的信息转化为杂乱的128位的编码,找到一种数据内容与地址之间的映射关系. 注意:不同的输入 ...