|
重用是一種神話,這似乎正在日漸成為編程人員的一種共識。然而,重用可能難以實(shí)現(xiàn),因?yàn)閭鹘y(tǒng)面向?qū)ο缶幊谭椒ㄔ诳芍赜眯苑矫娲嬖谝恍┎蛔恪1炯记烧f明了組成支持重用的一種不同方法的三個(gè)步驟。 第一步:將功能移出類實(shí)例方法 執(zhí)行單一概念性任務(wù)的任何方法都應(yīng)該是獨(dú)立的,并應(yīng)將其作為要重用的首選方法。要實(shí)現(xiàn)這一點(diǎn),我們必須返回到過程式編程,將代碼移出類實(shí)例方法并將其移入全局可見的過程中。為了提高這類過程的可重用性,您應(yīng)該像編寫靜態(tài)實(shí)用方法那樣編寫這類方法:每個(gè)過程只使用其自身的輸入?yún)?shù)和/或?qū)ζ渌挚梢娺^程的調(diào)用完成其工作,而且不應(yīng)該使用任何非局部變量。這種外部依賴性的減弱降低了使用該過程的復(fù)雜性,從而可促進(jìn)在別處對它的重用。當(dāng)然,即便那些不計(jì)劃重用的代碼也會(huì)從這種結(jié)構(gòu)中受益,因?yàn)樗慕Y(jié)構(gòu)總是相當(dāng)清晰。 在 Java 中,方法不能脫離類而存在。但是,您可以采取相關(guān)步驟,使方法成為單個(gè)類的、公共可見的靜態(tài)方法。作為示例,您可以采用類似下面這樣的一個(gè)類: class Polygon {
class Polygon {
class pPolygon {
在以上事例中所作更改的全部效果就是,客戶端代碼不再非要通過繼承 Polygon 來重用其功能。現(xiàn)在這一功能在 pPolygon 類中是以過程為單位提供的。客戶端代碼僅使用它所需的功能,而不必關(guān)心它不需要的功能。 這并不意味著類不會(huì)在新的過程式編程風(fēng)格中發(fā)揮積極作用。恰恰相反,類要執(zhí)行必要的分組任務(wù),并封裝它們所代表的對象的數(shù)據(jù)成員。此外,類通過實(shí)現(xiàn)多個(gè)接口而具備的多態(tài)性使其具備了卓越的可重用性,請參閱第二步中的說明。但是,您應(yīng)該將通過類繼承獲得可重用性和多態(tài)性的方法歸類到優(yōu)先級較低的技術(shù)中,因?yàn)閷⒐δ馨趯?shí)例方法中并不是實(shí)現(xiàn)可重用性的最佳選擇。 四人合著的暢銷書 Design Patterns 簡要提及了一種與這一技術(shù)只有細(xì)微差別的技術(shù)。那本書中的 Strategy 模式提倡用一個(gè)共公接口將相關(guān)算法的每個(gè)系列成員都封裝起來,以便客戶端代碼可互換這些算法。因?yàn)橐环N算法通常被編寫為一個(gè)或幾個(gè)獨(dú)立的過程,因而這種封裝強(qiáng)調(diào)重用執(zhí)行單一任務(wù)(即一個(gè)算法)的過程,而不強(qiáng)調(diào)重用包含代碼和數(shù)據(jù)、執(zhí)行多項(xiàng)任務(wù)的對象。本步驟也體現(xiàn)了同樣的基本思想。 然而,用接口封裝算法意味著將算法編寫為實(shí)現(xiàn)該接口的一個(gè)對象。這意味著我們?nèi)匀槐皇`在與數(shù)據(jù)耦合在一起的過程及其封裝對象的其他方法上,因而使重用變得復(fù)雜。每次使用算法時(shí)必須實(shí)例化這些對象也是個(gè)問題,這將降低程序的性能。幸運(yùn)的是, Design Patterns 提供的一種解決方案可解決這兩個(gè)問題。在編寫 Strategy 對象時(shí)您可使用 Flyweight 模式,以使每個(gè)對象僅有一個(gè)眾所周知的共享實(shí)例(該實(shí)例處理執(zhí)行問題),這樣每個(gè)共享對象就不會(huì)在兩次訪問之間維護(hù)狀態(tài)(因此該對象不包含任何成員變量,從而解決了許多耦合問題)。生成的 Flyweight-Strategy 模式將本步驟中封裝功能的技術(shù)高度集成在全局可用的無狀態(tài)過程中。 第二步:將非基本數(shù)據(jù)類型的輸入?yún)?shù)類型轉(zhuǎn)換為接口類型 “... 可重用性是通過編寫接口,而不是通過編寫類來實(shí)現(xiàn)的。如果一個(gè)方法的所有參數(shù)均為一些已知接口的引用,而這些接口又是由您從未聽過的一些類實(shí)現(xiàn)的,那么該方法可對編寫代碼時(shí)還不存在的類的對象進(jìn)行操作。從技術(shù)上講,可重用的是方法,而不是傳遞給該方法的對象。” 將 Holub 的論述應(yīng)用到第一步的結(jié)果,一旦某個(gè)功能塊可作為一個(gè)全局可見的獨(dú)立過程,您就可以通過將它的每個(gè)類級輸入?yún)?shù)類型轉(zhuǎn)換為接口類型,從而進(jìn)一步提高它的可重用性。這樣,實(shí)現(xiàn)該接口類型的任何類的對象都符合該參數(shù)的要求,而不僅僅是符合原始類的要求。這樣,該過程便潛在地可用于更多的對象類型。 例如,假定您有一個(gè)全局可見的靜態(tài)方法: static public boolean contains(Rectangle rect, int x, int y) {...}
static public boolean contains(Rectangular rect, int x, int y) {...}
public interface Rectangular {
但是,就以上示例而言,當(dāng) Rectangle 接口的 getBounds 方法返回一個(gè) Rectangle 時(shí),您可能不知道使用 Rectangular 接口會(huì)有什么實(shí)際的好處;也就是說,如果我們知道我們要傳入的對象在被請求時(shí)能返回 Rectangle;為什么不傳入 Rectangle 類型而要傳入接口類型呢?最重要的原因與集合有關(guān)。假定有這樣一個(gè)方法: static public boolean areAnyOverlapping(Collection rects) {...}
第三步:選擇耦合性較小的輸入?yún)?shù)接口類型 static public boolean areOverlapping(Window window1, Window window2) {...}
static public boolean areOverlapping(Rectangular rect1, Rectangular rect2) {...}
您可能有過多次這樣的經(jīng)歷,即充分指定了參數(shù)要求的可用接口包含過多不必要的方法。碰到這種情況時(shí),您就應(yīng)在全局名稱空間中定義一個(gè)新的公共接口,以便其他可能面臨同樣窘境的方法重用這個(gè)接口。 您也可能有過多次這樣的經(jīng)歷,即最好創(chuàng)建一個(gè)獨(dú)特的接口來指定單個(gè)過程對一個(gè)參數(shù)的要求。您所創(chuàng)建的接口只會(huì)用于那個(gè)參數(shù)。當(dāng)您希望將參數(shù)當(dāng)作 C 中的函數(shù)指針處理時(shí)經(jīng)常會(huì)出現(xiàn)這種情況,例如,假定有這樣一個(gè)過程: static public void sort(List list, SortComparison comp) {...}
public interface SortComparison {
以上三步旨在改進(jìn)用更傳統(tǒng)的面向?qū)ο蠓椒ň帉懙默F(xiàn)有代碼。將這三個(gè)步驟與面向?qū)ο缶幊探Y(jié)合使用即可構(gòu)建一種新的方法,您可用這種新方法編寫以后的代碼,這樣編寫代碼將提高方法的可重用性和內(nèi)聚性,同時(shí)也會(huì)減少方法的相互耦合及復(fù)雜性。 很明顯,您不應(yīng)該對本質(zhì)上不適合重用的代碼執(zhí)行這些步驟。這種代碼通常存在于程序的表示層。創(chuàng)建程序用戶界面的代碼及將輸入事件綁定到完成實(shí)際操作的控制代碼是不可重用的兩個(gè)例子,因?yàn)樗鼈兊墓δ茈S程序的不同而相差甚遠(yuǎn),根本無法實(shí)現(xiàn)可重用性。 |
|
|