你直接访问一个值域(field),但与值域之间的耦合关系逐渐变得笨拙。

为这个值域建立取值/设值函数(getting/setting methods),并且只以这些函数来访问值域。

private int _low, _high;
boolean includes(int arg) {
return arg >= _low && arg <= _high;
}

==〉

private int _low, _high;
boolean includes(int arg) {
return arg >= getLow() && arg <= getHigh();
}
int getLow() {return _low;}
int getHigh() {return _high;}

动机

如果你想访问superclass中的一个值域,却又想在subclass中将[对这个变量的访问]改为一个计算后的值,这就是最该使用Self Encapsulate Field(171)的时候。[值域自我封装]只是第一步。完成自我封装之后,你可以在subclass中根据自己的需要随意覆写取值/设值函数(getting/setting methods)。

作法

1. 为[待封装值域]建立取值/设值函数(getting/setting methods)。

2. 找出该值域的所有引用点,将它们全部替换为[对于取值/设值函数的调用]。

如果引用点是[读值]值域值,就将它替换为[调用取值函数];如果引用点是[设定]值域值,就将它替换为[调用设值函数]。

你可以暂时为设值域改名,让编译器帮助你查找引用点。

3. 将该值域声明为private。

4. 复查,确保找出所有引用点。

5. 编译,测试。

class IntRange {
private int _low, _high;

boolean includes(int arg) {
return arg >= _low && arg <= _high;
}

void grow(int factor) {
_high = _high * factor;
}

IntRange(int low, int high) {
_low = low;
_high = high;
}
}

为了封装_low和_high这两个值域,我先定义[取值/设值函数](如果此前没有定义的话),并使用它们:
class IntRange {
private int _low, _high;

boolean includes(int arg) {
return arg >= _low && arg <= _high;
}

void grow(int factor) {
SetHigh(getHigh()*factor);
}

int getLow() {
return _low;
}

int getHigh() {
return _high;
}

void setLow(int arg) {
_low = arg;
}

void setHigh(int arg) {
_high = arg;
}
}

使用本项重构时,一般说来,设值函数被认为应该在[对象创建后]才使用,所以初始化过程中的行为有可能与设值函数的行为不同。这种情况下,我允许在构造函数中直接访问值域,要不就是建立另一个独立的初始化函数:

IntRange(int low, int high) {
initialize(low, high);
}
private void initialize(int low, int high) {
_low = low;
_high = high;
}

一旦你拥有一个subclass,上述所有动作的价值就体现出来了。如下所示:

class CappedRange extends IntRange {
CappedRange(int low, int high, int cap) {
super(low, high);
_cap = cap;
}

private int _cap;

int getCap() {
return _cap;
}

int getHigh() {
return Math.min(super.getHigh(), getCap());
}
}
现在,我可以CappedRange class中覆写getHigh(),从而加入对cap的考虑,而不必修改IntRange class的任何行为。

重构改善既有代码设计--重构手法18:Self Encapsulate Field (自封装字段)的更多相关文章

  1. 重构改善既有代码设计--重构手法11:Move Field (搬移字段)

    你的程序中,某个字段被其所驻类之外的另一个类更多的用到.在目标类建立一个新字段,修改源字段的所有用户,令它们改用新字段.        动机:在类之间移动状态和行为,是重构过程中必不可少的措施.随着系 ...

  2. 重构改善既有代码设计--重构手法16:Introduce Foreign Method (引入外加函数)&& 重构手法17:Introduce Local Extension (引入本地扩展)

    重构手法16:Introduce Foreign Method (引入外加函数)你需要为提供服务的类增加一个函数,但你无法修改这个类.在客户类中建立一个函数,并以第一参数形式传入一个服务类实例. 动机 ...

  3. 重构改善既有代码设计--重构手法08:Replace Method with Method Object (以函数对象取代函数)

    你有一个大型函数,其中对局部变量的使用,使你无法釆用 Extract Method. 将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的值域(field) 然后你可以在同一个对象中将这个大型 ...

  4. 重构改善既有代码设计--重构手法07:Remove Assignments to Parameters (移除对参数的赋值)

    代码对一个 参数赋值.以一个临时变量取代该参数的位置.     int Discount(int inputVal, int quantity, int yearTodate) { if (input ...

  5. 重构改善既有代码设计--重构手法05:Introduce Explaining Variable (引入解释性变量)

      发现:你有一个复杂的表达式. 解决:将该复杂的表达式(或其中的部分)的结果放进一个临时变量,并以此变量名称来解释表达式用途. //重构前 if((platform.toUpperCase().in ...

  6. 重构改善既有代码设计--重构手法04:Replace Temp with Query (以查询取代临时变量)

    所谓的以查询取代临时变量:就是当你的程序以一个临时变量保存某一个表达式的运算效果.将这个表达式提炼到一个独立函数中.将这个临时变量的所有引用点替换为对新函数的调用.此后,新函数就可以被其他函数调用. ...

  7. 重构改善既有代码设计--重构手法02:Inline Method (内联函数)& 03: Inline Temp(内联临时变量)

    Inline Method (内联函数) 一个函数调用的本体与名称同样清楚易懂.在函数调用点插入函数体,然后移除该函数. int GetRating() { return MoreThanfiveLa ...

  8. 重构改善既有代码设计--重构手法01:Extract Method (提炼函数)

    背景: 你有一段代码可以被组织在一起并独立出来.将这段代码放进一个独立函数,并让函数名称解释该函数的用途. void PrintOwing(double amount) { PrintBanner() ...

  9. 重构改善既有代码设计--重构手法19:Replace Data Value with Object (以对象取代数据值)

    你有一笔数据项(data item),需要额外的数据和行为. 将这笔数据项变成一个对象. class Order... private string customer; ==> class Or ...

随机推荐

  1. NSURLErrorDomain错误代码

    kCFURLErrorUnknown = -998, kCFURLErrorCancelled = -999, kCFURLErrorBadURL = -1000, kCFURLErrorTimedO ...

  2. [codecademy]fonts in css

    Great job! You learned how to style an important aspect of the user experience: fonts! Let's review ...

  3. java下执行mongodb

    1.1连单台mongodb Mongo mg = newMongo();//默认连本机127.0.0.1  端口为27017 Mongo mg = newMongo(ip);//可以指定ip 端口默认 ...

  4. DB2 日志

    跟Oracle类似DB2也分为两个模式,日志循环vs归档日志,也就是非归档和归档模式,下面对这两种模式做简单的介绍. 日志循环 日志循环是默认方式,也就是非归档模式,这种模式只支持backup off ...

  5. QSet使用及Qt自定义类型使用QHash等算法

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:QSet使用及Qt自定义类型使用QHash等算法     本文地址:http://techie ...

  6. Node.js系列——(3)连接DB

    背景 node.js,有人称之为运行在服务器端的JavaScript.以往我们使用JavaScript时,都是依赖后端查询数据库并返回数据,而JavaScript只需要展示即可.问题来了,就不能绕开后 ...

  7. 把握曝光三要素(上):快门、光圈、ISO

    概要: 如果你还没有掌握快门.光圈和ISO,那这篇文章或许对你有所帮助! 把照片比作水池.把进光量比作水.把快门比作关闭水龙头的速度.把光圈比作水龙头的大小.把感光度ISO比作水龙头的滤网,这就变得好 ...

  8. spring cloud & dubbo

    区别 来源(背景): Dubbo,是阿里巴巴服务化治理的核心框架,并被广泛应用于阿里巴巴集团的各成员站点. Spring Cloud,从命名我们就可以知道,它是Spring Source的产物,Spr ...

  9. mvc4扩展方法

    制作扩展方法,方便网页中使用,下面做了两个例子 using System; using System.Collections.Generic; using System.Linq; using Sys ...

  10. Android四大组件之Intent(续2)

    1.你如何通过一个intent来唤醒activity? this.startActivity(intent,request);      2.什么是显式.隐式的intents? 显式:指定组件名,通常 ...