現(xiàn)在回到重構(gòu)--改善既有代碼的設(shè)計(jì)這本書。
3.4Long Parameter List(過長(zhǎng)參數(shù)列)
如果“向既有對(duì)象發(fā)出一條請(qǐng)求”就可以取得原本位于參數(shù)列上的一份數(shù)據(jù),那么你應(yīng)該激活重構(gòu)準(zhǔn)則Replace Parameter with Method。上述的既有對(duì)象可能是函數(shù)所屬class內(nèi)的一個(gè)值域,也可能是另一個(gè)參數(shù)。你還可以用 Preserve Whole Object將來自同一對(duì)象的一堆數(shù)據(jù)收藏起來,并以該對(duì)象替換它們。如果某些數(shù)據(jù)缺乏合理的對(duì)象歸屬,可使用Introduce Parameter Object為它們制造出一個(gè)“參數(shù)對(duì)象”。
Replace Parameter with Methods(以函數(shù)取代參數(shù))
對(duì)象調(diào)用某個(gè)函數(shù),并將結(jié)果作為參數(shù),傳遞給另外一個(gè)函數(shù)。而接受該參數(shù)的函數(shù)也可以(也有能力)調(diào)用前一個(gè)函數(shù)。
動(dòng)機(jī)
如果函數(shù)可以通過其它途徑(而非參數(shù)列)獲得參數(shù)值,那么它就不應(yīng)該通過參數(shù)取得該值。
縮減參數(shù)列的辦法之一就是,看看“參數(shù)接受端”是否可以通過“與調(diào)用端相同的計(jì)算”來取得參數(shù)攜帶值。如果調(diào)用端通過“其所屬對(duì)象內(nèi)部的另一個(gè)函數(shù)”來計(jì)算參數(shù),并在計(jì)算過程中“未曾飲用調(diào)用端的其它參數(shù)”,那么你就應(yīng)該將這個(gè)計(jì)算過程轉(zhuǎn)移到被調(diào)用端內(nèi),從而除去該項(xiàng)參數(shù)。如果你所調(diào)用的函數(shù)隸屬另一對(duì)象,而該對(duì)象擁有一個(gè)reference指向調(diào)用端所屬對(duì)象,前面所說的這些也同樣適用。
作法
1、如果有必要,將參數(shù)的計(jì)算過程提煉到一個(gè)獨(dú)立函數(shù)中。
2、將函數(shù)本體內(nèi)“對(duì)該參數(shù)的引用”替換為對(duì)“新函數(shù)的引用”。
3、每次替換后,修改并測(cè)試。
4、全部替換完成后,適用Remove Parameter將該參數(shù)去掉。
思考:
確實(shí)從實(shí)踐角度講,上面這種方法是一種很實(shí)用的重構(gòu)方法。確實(shí)是縮減了函數(shù)的參數(shù)個(gè)數(shù),并隱藏了一些中間結(jié)果。但是我認(rèn)為,在應(yīng)用這個(gè)方法的時(shí)候,還應(yīng)當(dāng)考慮這樣兩個(gè)問題:
1、當(dāng)中間函數(shù)(方法)被隱藏的時(shí)候,以這個(gè)中間函數(shù)的返回值為參數(shù)的那個(gè)函數(shù)名還能夠表述自己的用途嗎?
2、被修改的那個(gè)函數(shù)所處的具體語境是什么。因?yàn)樵诿嫦驅(qū)ο缶幊痰臅r(shí)代,單獨(dú)去考慮一個(gè)函數(shù)的情況是很少的。確保這個(gè)函數(shù)是否處在正確的位置上(如是否處在正確的類中)是必要的。
此間存在一個(gè)重要的例外。有時(shí)候你明顯不希望造成“被調(diào)用之對(duì)象”與“較大對(duì)象”間的某種依存關(guān)系。這時(shí)候?qū)?shù)據(jù)從對(duì)象拆解出來單獨(dú)作為參數(shù),也很合情合理。但是請(qǐng)注意其所引發(fā)的代價(jià)。如果參數(shù)列太長(zhǎng)或變化太頻繁,你就需要重新考慮自己的依存結(jié)構(gòu)了。
Introduce Parameter Object(引入?yún)?shù)對(duì)象)
某些參數(shù)總是很自然的同時(shí)出現(xiàn),以一個(gè)對(duì)象取代這些參數(shù)。
你常會(huì)看到特定的一組參數(shù)總是一起被傳遞。可能有好幾個(gè)函數(shù)都使用這一組參數(shù),這些函數(shù)可能隸屬同一個(gè)class,也可能隸屬不同的classes。這樣一組參數(shù)就是所謂的Data Clump(數(shù)據(jù)泥團(tuán))。我們可以運(yùn)用一個(gè)對(duì)象包裝所有這些數(shù)據(jù),再以該對(duì)象取代它們。
作法:
1、新建一個(gè)class,用以表現(xiàn)你想替換的一組參數(shù)。將這個(gè)class設(shè)為不可變的(不可被修改的,immutable)。
2、編譯。
3、針對(duì)使用該組參數(shù)的所有函數(shù),實(shí)施Add Parameter,以上述新建class之實(shí)體對(duì)象作為新添參數(shù),并將此一參數(shù)值設(shè)為null。
4、對(duì)于Data Clump(數(shù)據(jù)泥團(tuán))中的每一項(xiàng)(在此均為參數(shù)),從函數(shù)簽名式中移除之,并修改調(diào)用端和函數(shù)本體,令他們都改而通過“新建的參數(shù)對(duì)象” 取得該值。
5、每除去一個(gè)參數(shù),編譯并測(cè)試。
6、將原先的參數(shù)全部除去之后,觀察有無適當(dāng)函數(shù)可以運(yùn)用Move Method搬移到參數(shù)對(duì)象中。
思考:
現(xiàn)在我有一個(gè)函數(shù)f,函數(shù)定義如下:
public string f(int i,int j,string s)
{
int newValue=i+j;
string result=s+":"+newValue.ToString();
return result;
}
現(xiàn)在我把這三個(gè)參數(shù)轉(zhuǎn)化為類對(duì)象,類名定義為MyParameter
public class MyParameter
{
int _i;
public int I
{
get { return _i; }
set { _i = value; }
}
int _j;
public int J
{
get { return _j; }
set { _j = value; }
}
string _s;
public string S
{
get { return _s; }
set { _s = value; }
}
public MyParameter(int i, int j, stirng s)
{
_i = i;
_j = j;
_s = s;
}
}
然后再我的代碼中這樣寫:
f(new MyParameter(3,2,"Result"));
發(fā)現(xiàn)什么問題了嗎?
1、好像參數(shù)并沒有減少,只不過是移動(dòng)到了構(gòu)造函數(shù)中;
2、你從f參數(shù)中將會(huì)看不出f中到底需要什么東西(如果參數(shù)名稱體現(xiàn)了參數(shù)的用途的話)。
這說明:
1、雖然這是經(jīng)典的重構(gòu)方法,但是不能濫用,在使用的時(shí)候要謹(jǐn)慎考慮場(chǎng)景是否合適。
2、注意TDA原理,不要刻意產(chǎn)生太多的中間對(duì)象。
3、參數(shù)不能隨便合并或隱去,要特別注意領(lǐng)域模型,要切合領(lǐng)域模型所表述的意圖。
我個(gè)人認(rèn)為可以應(yīng)用的場(chǎng)景:
就是文中所說的DataRange,即適合于被轉(zhuǎn)換為參數(shù)對(duì)象的參數(shù)列具有通用的含義,并且概念層次比較低。
posted on 2007-07-10 22:16
littlegai 閱讀(275)
評(píng)論(0) 編輯 收藏 引用 所屬分類:
我的讀書筆記