- 論壇徽章:
- 0
|
Chain Constructors(串聯(lián)構(gòu)造子)
撰文/Joshua Kerievsky 編譯/透明
你擁有多個(gè)構(gòu)造子,其中包含了重復(fù)的代碼。
將構(gòu)造子串在一起,以使重復(fù)代碼減到最少。
public class Loan {
...
public Loan(float notional, float outstanding, int rating, Date expiry) {
this.strategy = new TermROC();
this.notional = notional;
this.outstanding = outstanding;
this.rating =rating;
this.expiry = expiry;
}
public Loan(float notional, float outstanding, int rating, Date expiry, Date maturity) {
this.strategy = new RevolvingTermROC();
this.notional = notional;
this.outstanding = outstanding;
this.rating = rating;
this.expiry = expiry;
this.maturity = maturity;
}
public Loan(CapitalStrategy strategy, float notional, float outstanding,
int rating, Date expiry, Date maturity) {
this.strategy = strategy;
this.notional = notional;
this.outstanding = outstanding;
this.rating = rating;
this.expiry = expiry;
this.maturity = maturity;
}
}
public class Loan {
...
public Loan(float notional, float outstanding, int rating, Date expiry) {
this(new TermROC(), notional, outstanding, rating, expiry, null);
}
public Loan(float notional, float outstanding, int rating, Date expiry, Date maturity) {
this(new RevolvingTermROC(), notional, outstanding, rating, expiry, maturity);
}
public Loan(CapitalStrategy strategy, float notional, float outstanding,
int rating, Date expiry, Date maturity) {
this.strategy = strategy;
this.notional = notional;
this.outstanding = outstanding;
this.rating = rating;
this.expiry = expiry;
this.maturity = maturity;
}
}
動(dòng)機(jī)
在同一個(gè)類的兩個(gè)或更多的構(gòu)造子中編寫重復(fù)代碼,這就是在為自己埋下麻煩的種子。別人會(huì)在你的類中添加新的變量,然后更新一個(gè)構(gòu)造子來(lái)對(duì)這個(gè)變量進(jìn)行初始化,但是卻忘了更新別的構(gòu)造子。于是,“砰”的一聲,向新的bug問(wèn)好吧。一個(gè)類中的構(gòu)造子越多,代碼的重復(fù)就會(huì)傷害你越重。如果有可能,就應(yīng)該盡量減少或去除代碼重復(fù),這樣做的額外好處就是可以幫助你的代碼系統(tǒng)減肥。
為了達(dá)到這個(gè)目標(biāo),我們經(jīng)常會(huì)使用Constructor Chaining模式進(jìn)行重構(gòu):特化的(specific)構(gòu)造子調(diào)用普化的(general-purposed)構(gòu)造子,重復(fù)這個(gè)過(guò)程,直到最普化的構(gòu)造子也被調(diào)用到。如果你的每條調(diào)用鏈的末端都是同一個(gè)構(gòu)造子,我就把它叫做“catch-all”構(gòu)造子,因?yàn)樗幚砹怂袠?gòu)造子的調(diào)用。這個(gè)catch-all構(gòu)造子通常會(huì)比其他構(gòu)造子接受更多的參數(shù),并且可能是(也可能不是)私有的或保護(hù)的。
如果你發(fā)現(xiàn)多個(gè)構(gòu)造子降低了類的可用性,請(qǐng)考慮使用“用Factory Method模式替換多個(gè)構(gòu)造子”的重構(gòu)方法。
通信
重復(fù)
簡(jiǎn)化
如果一個(gè)類中的多個(gè)構(gòu)造子在實(shí)現(xiàn)重復(fù)的工作,那么在特化與普化的通信上,你的代碼就是失敗的。要實(shí)現(xiàn)這種通信,就應(yīng)該讓特化的構(gòu)造子調(diào)用普化的,并讓每個(gè)構(gòu)造子都做自己獨(dú)一無(wú)二的工作。
構(gòu)造子中的重復(fù)代碼讓你的類更容易出錯(cuò)、更難維護(hù)。尋找通用的功能,將它放到普化的構(gòu)造子中,將調(diào)用轉(zhuǎn)發(fā)給這些普化的構(gòu)造子,并在其他的構(gòu)造子中實(shí)現(xiàn)用途不廣泛的功能。
如果超過(guò)一個(gè)的構(gòu)造子包含同樣的代碼,要看出構(gòu)造子之間的區(qū)別就很困難了。讓特化的構(gòu)造子調(diào)用普化的,并形成一條調(diào)用鏈,從而對(duì)構(gòu)造子進(jìn)行簡(jiǎn)化。
過(guò)程
1. 尋找包含重復(fù)代碼的兩個(gè)構(gòu)造子(我把它們分別叫做A和B)。確定A是否可以調(diào)用B或者B是否可以調(diào)用A,這樣重復(fù)代碼才可以被安全的(和比較容易的)從這某一個(gè)構(gòu)造子中刪掉。
2. 編譯、測(cè)試。
3. 對(duì)類中的每個(gè)構(gòu)造子,重復(fù)步驟1和2,以獲得盡量少的重復(fù)代碼。
4. 如果某個(gè)構(gòu)造子不必要成為公開(kāi)的,就改變它的可見(jiàn)性。
5. 編譯、測(cè)試。
范例
1. 我們將從一段簡(jiǎn)單代碼——一個(gè)Loan類——開(kāi)始。這個(gè)類有三個(gè)構(gòu)造子,用來(lái)表現(xiàn)三種不同類型的貸款業(yè)務(wù)。大量丑陋的重復(fù)代碼。
public Loan(float notional, float outstanding, int rating, Date expiry) {
this.strategy = new TermROC();
this.notional = notional;
this.outstanding = outstanding;
this.rating = rating;
this.expiry = expiry;
}
public Loan(float notional, float outstanding, int rating, Date expiry, Date maturity) {
this.strategy = new RevolvingTermROC();
this.notional = notional;
this.outstanding = outstanding;
this.rating = rating;
this.expiry = expiry;
this.maturity = maturity;
}
public Loan(CapitalStrategy strategy, float notional, float outstanding, int rating,
Date expiry, Date maturity) {
this.strategy = strategy;
this.notional = notional;
this.outstanding = outstanding;
this.rating = rating;
this.expiry = expiry;
this.maturity = maturity;
}
我研究了前面的兩個(gè)構(gòu)造子。它們包含重復(fù)的代碼,而第三個(gè)構(gòu)造子也同樣。我考慮第一個(gè)構(gòu)造子應(yīng)該調(diào)用哪一個(gè),并發(fā)現(xiàn)它應(yīng)該調(diào)用第三個(gè)構(gòu)造子。因此我把第一個(gè)構(gòu)造子修改成:
public Loan(float notional, float outstanding, int rating, Date expiry) {
this(new TermROC(), notional, outstanding, rating, expiry, null);
}
2. 編譯程序,測(cè)試這次修改是否正常工作。
3. 重復(fù)步驟1和2,盡量去除代碼重復(fù)。這引出了第二個(gè)構(gòu)造子,它也調(diào)用第三個(gè)構(gòu)造子,如下所示:
public Loan(float notional, float outstanding, int rating, Date expiry, Date maturity) {
this(new RevolvingTermROC(), notional, outstanding, rating, expiry, maturity);
}
現(xiàn)在我知道第三個(gè)構(gòu)造子就是我的catch-all構(gòu)造子,因?yàn)樗幚砹怂械臉?gòu)造細(xì)節(jié)。
4. 檢查這三個(gè)構(gòu)造子所有的調(diào)用者,以確定是否可以改變某一個(gè)的可見(jiàn)性。在這個(gè)案例中,我不能這樣做(假設(shè)是這樣——在這里你無(wú)法知道其他代碼的情況)。
5. 編譯并測(cè)試,完成此次重構(gòu)。
參考書(shū)目
[Alex77] Alexander, C., Ishikawa, S., Silverstein, M., A Pattern Language, New York: Oxford University Press, 1977.
[Fowler99] Fowler, M., Beck, K., Brant, J., Opdyke, W., Roberts, D., Refactoring: Improving the Design of Existing Code, Reading Mass.: Addison-Wesley, 1999.
[GoF95] Gamma, E., Heml, R., Johnson, R., Vlissides, J., Design Patterns: Elements of Reusable Object-Oriented Software, Reading Mass.: Addison-Wesley, 1995. 中譯本:《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》,李英軍等譯,機(jī)械工業(yè)出版社,2000年9月。
|
|