#
范例(Examples)
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) {
_high = _high * 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的任何行為。
作法(Mechanics)
- 為[待封裝值域]建立取值/設值函數(getting/setting methods)。
- 找出該值域的所有引用點,將它們全部替換為[對于取值/設值函數的調用]。
- ==》如果引用點是[讀值]值域值,就將它替換為[調用取值函數];如果引用點是[設定]值域值,就將它替換為[調用設值函數]。
- ==》你可以暫時為設值域改名,讓編譯器幫助你查找引用點。
- 將該值域聲明為private。
- 復查,確保找出所有引用點。
- 編譯,測試。
動機(Motivation)
如果你想訪問superclass中的一個值域,卻又想在subclass中將[對這個變量的訪問]改為一個計算后的值,這就是最該使用Self Encapsulate Field(171)的時候。[值域自我封裝]只是第一步。完成自我封裝之后,你可以在subclass中根據自己的需要隨意覆寫取值/設值函數(getting/setting methods)。
你直接訪問一個值域(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;}
首先聲明一個wrapping class:
class MfDateWrap {
private Date _original;
}
使用wrapping方案時,我對構造函數的設定與先前有所不同。現在的構造函數將只是執行一個單純的委托動作(delegation):
public MfDateWrap(String dateString) {
_original = new Date(dateString);
}
而轉型構造函數則只是對其instance變量賦值而已:
public int getYear() {
return _original.getYear();
}
public boolean equals(MfDateWrap arg) {
return (toDate().equals(arg.toDate());
}
完成這項工作之后,我就可以后使用Move Method(142)將日期相關行為搬移到新class中。于是以下代碼:
client class...
private static Date nextDay(Date arg) {
return new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1);
}
經過搬移之后,就成了:
class MfDate...
Date nextDay() {
return new Date(getYear(), getMonth(), getDate() + 1);
}
首先,我要建立一個新的MfDateSub class來表示[日期],并使其成為Date的subclass:
class MfDateSub extends Date
然后,我需要處理Date和我的extension class之間的不同處。MfDateSub構造函數需要委托(delegating)給Date構造函數:
public MfDateSub(String dateString) {
super(dateString);
}
現在,我需要加入一個轉型構造函數,其參數是一個隸屬原類的對象:
public MfDateSub(Date arg){
super(arg.getTime());
}
現在,我可以在extension class中添加新特性,并使用Move Method(142)將所有外加函數(foreign method)搬移到extension class。于是,下面的代碼:
client class...
private static Date nextDay(Date arg) {
return new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1);
}
經過搬移之后,就成了:
class MfDate...
Date nextDay() {
return new Date(getYear(), getMonth(), getDate() + 1);
}
我將以Java1.0.1的Date class為例。Java 1.1已經提供了我想要的功能,但是在它到來之前的那段日子,很多時候需要擴展Java 1.0.1的Date class。
第一件待決事項是使用subclass或wrapper。subclassing是比較顯而易見的辦法:
class mfDate extends Date {
public nextDay() ...
public dayOfYear() ...
}
wrapper則需要上委托(delegation):
class mfDate {
private Date _original;
作法(Mechanics)
- 建立一個extension class,將它作為原物(原類)的subclass或wrapper。
- 在extension class中加入轉型構造函數(converting constructors)。
- ==》所謂[轉型構造函數]是指接受原物(original)作為參數。如果你采用subclassing方安,那么轉型構造函
數應該調用適當的superclass構造函數;如果你采用wrapper方案,那么轉型構造函數應該將它所獲得之引數(arument)賦值給[用以保
存委托關系(delegate)]的那個值域。
- 在extension class中加入新特性。
- 根據需要,將原物(original)替換為擴展物(extension)。
- 將[針對原始類(original class)而定義的所有外加函數(foreign methods)]搬移到擴展類(extension)中。
動機(Motivation)
如果你需要的額外函數超過兩個,外加函數(foreign
methods)就很難控制住它們了。所以,你需要將這些函數組織在一起,放到一個恰當地方去。要達到這一目的,標準對象技術subclassing和
wrapping是顯而易見的辦法。這種情況下我把subclass或wrapper稱為local extension(本地擴展)。
在subclass和wrapper之間做選擇時,我通常首先subclass。
你所使用的server class需要一些額外函數,但你無法修改這個class。
建立一個新class,使它包含這些額外函數。讓這個擴展品成為source class的subclass(子類)或wrapper(外覆類)。

|