敏捷軟件開發(fā) - 原則、模式與實踐 —— 設(shè)計模式(十一)VISITOR模式

本文為敏捷軟件開發(fā) - 原則、模式與實踐系列的一部分。

本文對應(yīng)原書第28章

VISTOR模式系列允許在不更改現(xiàn)有層次結(jié)構(gòu)的情況下向其中增加新方法。

該系列中的模式如下

  • VISITOR模式
  • ACYCLIC VISITOR模式
  • DECORATOR模式
  • EXTENSION ObJECT模式

VISITOR模式

圖1

這個結(jié)構(gòu),可以通過增加新的ModemVisitor派生類來增加新的操作系統(tǒng)配置函數(shù),而完全不用對Modem層次結(jié)構(gòu)進(jìn)行更改。所以,VISITOR模式使用ModemVisitor的派生類代替了Modem層次結(jié)構(gòu)中的方法。

這之所以被稱為雙重分發(fā)是因為它涉及了兩個多態(tài)分發(fā)。第一個分發(fā)是accept函數(shù)。該分發(fā)辨別出所調(diào)用的accept方法所屬對象的類型。第二個分發(fā)是visit方法,它辨別出要執(zhí)行的特定函數(shù)。這兩個分發(fā)賦予了VISITOR模式非常快的執(zhí)行速度。

VISITOR模式中的兩次分發(fā)形成了一個功能矩陣。在Modem的例子中,矩陣的一條軸是不同類型的Modem。另一條軸是不同類型的操作系統(tǒng)。該矩陣的每個單元都被一項功能填充,該功能描繪了如何把特定的Modem初始化為可以在特定的操作系統(tǒng)中使用。

ACYCLIC VISITOR模式

如果程序中要更改的層次結(jié)構(gòu)不需要經(jīng)常地增加新的派生類,那么VISITOR模式工作的很好。但是,如果被訪問層次結(jié)構(gòu)非常不穩(wěn)定,經(jīng)常需要創(chuàng)建許多新的派生類,那么每當(dāng)向被訪問層次結(jié)構(gòu)中增加一個新的派生類時,就必須要更改并且重新編譯Visitor基類以及它的所有派生類。在C++中,情況甚至更糟。每當(dāng)增加任何一個新的派生類時,整個被訪問層次結(jié)構(gòu)就必須要被重新編譯、重新部署。

可以使用一個成為ACYCLIC VISITOR模式的變體來解決這個問題。該變體把Visitor基類變成退化的,從而解除了依賴環(huán)。這個類中沒有任何方法意味著它沒有依賴于被訪問層次結(jié)構(gòu)的派生類。

圖2

由于轉(zhuǎn)型需要花費大量的執(zhí)行時間,并且這些時間是不可預(yù)測的,所以ACYCLIC VISITOR模式不適用于嚴(yán)格的實時系統(tǒng)。該模式的復(fù)雜性可能同樣會使它不適用于其他的系統(tǒng)。但是,對于那些被訪問的層次結(jié)構(gòu)不穩(wěn)定,并且增量編譯比較重要的系統(tǒng)來說,該模式是一個不錯的選擇。

正像VISITOR模式創(chuàng)建了一個功能矩陣(一個軸是被訪問的類型,另一個軸是要執(zhí)行的功能)一樣,ACYCLIC VISITOR模式創(chuàng)建了一個稀疏矩陣。訪問者類不需要針對每一個被訪問的派生類都實現(xiàn)visit函數(shù)。例如,如果Ernie Modem不可以配置在UNIX中,那么UnixModemConfigurator就不會實現(xiàn)ErnieVisitor接口。因此,ACYCLIC VISITOR模式允許我們忽略某些派生類和功能的組合。有時,這可能是一個有用的優(yōu)點。

DECORATOR模式

另一個可以在不改變現(xiàn)有類層次結(jié)構(gòu)的情況下向其中增加新方法的模式是DECORATOR模式。

圖3

ModemDecorator子類的dial方法首先執(zhí)行自己的特殊操作,然后委托調(diào)用它包含的Modem實例
的dial方法。

EXTENSION OBJECT模式

還有另外一種方法可以在不更改類層次結(jié)構(gòu)的情況下向其中增加功能,那就是使用EXTENSION OBJECT模式。這個模式雖然比其他模式復(fù)雜一些,但是它也更強(qiáng)大、更靈活一些。層次結(jié)構(gòu)中的每個對象都持有一個特定擴(kuò)展對象(extension object)的列表。同時,每個對象也提供一個通過名字查找擴(kuò)展對象的方法。擴(kuò)展對象提供了操作原始層次結(jié)構(gòu)對象的方法。

例如,再次假設(shè)我們有一個材料單系統(tǒng)。我們想讓該層次結(jié)構(gòu)中的每個對象都具有創(chuàng)建表示自身的XML的能力。我們可以把toXML方法放到層次結(jié)構(gòu)中,但是這回違反SRP。我們不希望把有關(guān)XML的內(nèi)容和有關(guān)BOM的內(nèi)容放到同一個類中。雖然我們可以使用VISITOR模式來創(chuàng)建XML,但是這無法使我們把針對每種不同類型BOM對象的XML生成代碼分離。在VISITOR模式中,針對每個BOM類的所有XML生成代碼都會在同一個VISITOR對象中。

EXTENSION OBJECT模式提供了一個實現(xiàn)這個目標(biāo)的優(yōu)雅方案。下圖展示了具有兩個不同類型擴(kuò)展對象的BOM層次結(jié)構(gòu)。一種擴(kuò)展對象把BOM對象轉(zhuǎn)換成XML。另一個擴(kuò)展對象把BOM對象轉(zhuǎn)換成CSV。第一種擴(kuò)展對象通過getExtension("XML")獲得,第二種擴(kuò)展對象通過getExtension("CSV")獲得。圖中<<marker>>表示一個標(biāo)記接口(也就是沒有任何方法的接口)。

圖4

結(jié)論

VISITOR模式是由誘惑力的。在它們面前很容易會失去自制力。如果它們有用就去使用它們,但是請對它們的必要性保持健康的懷疑。通常,可以使用VISITOR模式解決的問題往往也可以使用更簡單的方法解決。

完整內(nèi)容請查看敏捷軟件開發(fā) - 原則、模式與實踐系列

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容