鮮花網(wǎng)站建設(shè)的目標(biāo)百度賬號(hào)
重構(gòu)類關(guān)系-Extract Interface提煉接口八
1.提煉接口
1.1.使用場(chǎng)景
若干客戶使用類接口中的同一子集,或者兩個(gè)類的接口有部分相同。將相同的子集提煉到一個(gè)獨(dú)立接口中。
類之間彼此互用的方式有若干種?!笆褂靡粋€(gè)類”通常意味用到該類的所有責(zé)任區(qū)。另一種情況是,某一組客戶只使用類責(zé)任區(qū)中的一個(gè)特定子集。再一種情況則是,這個(gè)類需要與所有協(xié)助處理某些特定請(qǐng)求的類合作。
對(duì)于后兩種情況,將真正用到的這部分責(zé)任分離出來通常很有意義,因?yàn)檫@樣可以使系統(tǒng)的用法更清晰,同時(shí)也更容易看清系統(tǒng)的責(zé)任劃分。如果新的類需要支持上述子集,也比較能夠看清子集內(nèi)有些什么東西。
在許多面向?qū)ο笳Z言中,這種責(zé)任劃分是通過多繼承(multiple inheritance)來實(shí)現(xiàn)的。你可以針對(duì)每組行為建立一個(gè)類,再將它們組合于同一個(gè)實(shí)現(xiàn)中。Java只提供單繼承(single inheritance),但你可以運(yùn)用接口(interface)來昭示并實(shí)現(xiàn)上述需求。接口對(duì)于Java程序的設(shè)計(jì)方式有著巨大的影響,就連Smalltalk程序員都認(rèn)為接口是一大進(jìn)步!
Extract Superclass (336)和Extract Interface (341)之間有些相似之處。Extract Interface (341)只能提煉共通接口,不能提煉共通代碼。使用Extract Interface (341)可能造成難聞的“重復(fù)”壞味道,幸而你可以運(yùn)用Extract Class (149)先把共通行為放進(jìn)一個(gè)組件中,然后將工作委托該組件,從而解決這個(gè)問題。如果有不少共通行為,Extract Superclass (336)會(huì)比較簡單,但是每個(gè)類只能有一個(gè)超類。
如果某個(gè)類在不同環(huán)境下扮演截然不同的角色,使用接口就是個(gè)好主意。你可以針對(duì)每個(gè)角色以Extract Interface (341)提煉出相應(yīng)接口。另一種可以用上Extract Interface (341)的情況是:你想要描述一個(gè)類的外部依賴接口(outbound interface,即這個(gè)類要求服務(wù)提供方提供的操作)。如果你打算將來加入其他種類的服務(wù)對(duì)象,只需要求它們實(shí)現(xiàn)這個(gè)接口即可。
1.2.如何做
- 新建一個(gè)空接口。
- 在接口中聲明待提煉類的共通操作。
- 讓相關(guān)的類實(shí)現(xiàn)上述接口。
- 調(diào)整客戶端的類型聲明,令其使用該接口。
1.3.示例
TimeSheet類表示員工為客戶工作的時(shí)間表,從中可以計(jì)算客戶應(yīng)該支付的費(fèi)用。為了計(jì)算這筆費(fèi)用,TimeSheet需要知道員工級(jí)別,以及該員工是否有特殊技能:
// 通過Employee類對(duì)象獲取員工信息
double charge(Employee emp, int days) {int base = emp.getRate() * days;if (emp.hasSpecialSkill())return base * 1.05;else return base;
}
除了提供員工的級(jí)別和特殊技能信息外,Employee還有很多其他方面的功能,但本應(yīng)用程序只需這兩項(xiàng)功能。我可以針對(duì)這兩項(xiàng)功能定義一個(gè)接口,從而強(qiáng)調(diào)“我只需要這部分功能”的事實(shí):
interface Billable {public int getRate();public boolean hasSpecialSkill();}
然后,我聲明讓Employee實(shí)現(xiàn)這個(gè)接口
class Employee implements Billable ...
完成以后,我可以修改charge()函數(shù)聲明,強(qiáng)調(diào)該函數(shù)只使用Employee的這部分行為
// Billable接口明確表示 只使用Employee類中部分功能
double charge(Billable emp, int days) {int base = emp.getRate() * days;if (emp.hasSpecialSkill())return base * 1.05;else return base;
}
到目前為止,我們只不過是在文檔化方面有一點(diǎn)收獲。單就這一個(gè)函數(shù)而言,這樣的收獲并沒有太大價(jià)值;但如果有若干個(gè)類都使用Billable接口,它就會(huì)很有用。如果我還想計(jì)算電腦租金,巨大的收獲就顯露出來了:要想計(jì)算客戶租用電腦的費(fèi)用,我只需讓Computer類實(shí)現(xiàn)Billable接口,然后就可以把租用電腦的時(shí)間也填到時(shí)間表上了。