一、引子
那么像引言中假想的。我們應(yīng)該做些什么才能讓訪問者模式跑起來呢?首先我們要在原有的類層次結(jié)構(gòu)中添加accept方法。然后將這個類層次中的類放到一個對象結(jié)構(gòu)中去。這樣再去創(chuàng)建訪問者角色…… 三、舉例 本人閱歷實在可憐,沒能找到訪問者模式在實際應(yīng)用中的例子。只好借《Thinking in Patterns with java》中的教學(xué)代碼一用。我稍微做了下修改。 import java.util.*; import junit.framework.*; //訪問者角色 interface Visitor { void visit(Gladiolus g); void visit(Runuculus r); void visit(Chrysanthemum c); } // The Flower hierarchy cannot be changed: //元素角色 interface Flower { void accept(Visitor v); } //以下三個具體元素角色 class Gladiolus implements Flower { public void accept(Visitor v) { v.visit(this);} } class Runuculus implements Flower { public void accept(Visitor v) { v.visit(this);} } class Chrysanthemum implements Flower { public void accept(Visitor v) { v.visit(this);} } // Add the ability to produce a string: //實現(xiàn)的具體訪問者角色 class StringVal implements Visitor { String s; public String toString() { return s; } public void visit(Gladiolus g) { s = "Gladiolus"; } public void visit(Runuculus r) { s = "Runuculus"; } public void visit(Chrysanthemum c) { s = "Chrysanthemum"; } } // Add the ability to do "Bee" activities: //另一個具體訪問者角色 class Bee implements Visitor { public void visit(Gladiolus g) { System.out.println("Bee and Gladiolus"); } public void visit(Runuculus r) { System.out.println("Bee and Runuculus"); } public void visit(Chrysanthemum c) { System.out.println("Bee and Chrysanthemum"); } } //這是一個對象生成器 //這不是一個完整的對象結(jié)構(gòu),這里僅僅是模擬對象結(jié)構(gòu)中的元素 class FlowerGenerator { private static Random rand = new Random(); public static Flower newFlower() { switch (rand.nextInt(3)) { default: case 0: return new Gladiolus(); case 1: return new Runuculus(); case 2: return new Chrysanthemum(); } } } //客戶 測試程序 public class BeeAndFlowers extends TestCase { /* 在這里你能看到訪問者模式執(zhí)行的流程: 首先在客戶端先獲得一個具體的訪問者角色 遍歷對象結(jié)構(gòu) 對每一個元素調(diào)用accept方法,將具體訪問者角色傳入 這樣就完成了整個過程 */ //對象結(jié)構(gòu)角色在這里才 組裝 上 List flowers = new ArrayList(); public BeeAndFlowers() { for(int i = 0; i < 10; i++) flowers.add(FlowerGenerator.newFlower()); } Visitor sval ; public void test() { // It’s almost as if I had a function to // produce a Flower string representation: //這個地方你可以修改以便使用另外一個具體訪問者角色 sval = new StringVal(); Iterator it = flowers.iterator(); while(it.hasNext()) { ((Flower)it.next()).accept(sval); System.out.println(sval); } } public static void main(String args[]) { junit.textui.TestRunner.run(BeeAndFlowers.class); } } 四、雙重分派 對了,你在上面的例子中體會到雙重分派的實現(xiàn)了沒有? 首先在客戶程序中將具體訪問者模式作為參數(shù)傳遞給具體元素角色(加亮的地方所示)。這便完成了一次分派。 進(jìn)入具體元素角色后,具體元素角 色調(diào) 用作為參數(shù)的具體訪問者模式中的visitor方法,同時將自己(this)作為參數(shù)傳遞進(jìn)去。具體訪問者模式再根據(jù)參數(shù)的不同來選擇方法來執(zhí)行(加亮的地方所示)。這便完成了第二次分派。 五、優(yōu)缺點及適用情況 先來看下訪問者模式的使用能否避免引言中的痛苦。使用了訪問者模式以后,對于原來的類層次增加新的操作,僅僅需要實現(xiàn)一個具體訪問者角色就可以了,而不必修改整個類層次。而且這樣符合“開閉原則”的要求。而且每個具體的訪問者角色都對應(yīng)于一個相關(guān)操作,因此如果一個操作的需求有變,那么僅僅修改一個具體訪問者角色,而不用改動整個類層次。 看來訪問者模式確實能夠解決我們面臨的一些問題。 而且由于訪問者模式為我們的系統(tǒng)多提供了一層“訪問者”,因此我們可以在訪問者中添加一些對元素角色的額外操作。 但是“開閉原則”的遵循總是片面的。如果系統(tǒng)中的類層次發(fā)生了變化,會對訪問者模式產(chǎn)生什么樣的影響呢?你必須修改訪問者角色和每一個具體訪問者角色…… 看來訪問者角色不適合具體元素角色經(jīng)常發(fā)生變化的情況。而且訪問者角色要執(zhí)行與元素角色相關(guān)的操作,就必須讓元素角色將自己內(nèi)部屬性暴露出來,而在java中就意味著其它的對象也可以訪問。這就破壞了元素角色的封裝性。而且在訪問者模式中,元素與訪問者之間能夠傳遞的信息有限,這往往也會限制訪問者模式的使用。 《設(shè)計模式》一書中給出了訪問者模式適用的情況: 1) 一個對象結(jié)構(gòu)包含很多類對象,它們有不同的接口,而你想對這些對象實施一些依賴于其具體類的操作。 2) 需要對一個對象結(jié)構(gòu)中的對象進(jìn)行很多不同的并且不相關(guān)的操作,而你想避免讓這些操作“污染”這些對象的類。Visitor使得你可以將相關(guān)的操作集中起來定義在一個類中。 3) 當(dāng)該對象結(jié)構(gòu)被很多應(yīng)用共享時,用Visitor模式讓每個應(yīng)用僅包含需要用到的操作。 4) 定義對象結(jié)構(gòu)的類很少改變,但經(jīng)常需要在此結(jié)構(gòu)上定義新的操作。改變對象結(jié)構(gòu)類需要重定義對所有訪問者的接口,這可能需要很大的代價。如果對象結(jié)構(gòu)類經(jīng)常改變,那么可能還是在這些類中定義這些操作較好。 你是否能很好的理解呢? 六、總結(jié) 這是一個巧妙而且復(fù)雜的模式,它的使用條件比較苛刻。當(dāng)系統(tǒng)中存在著固定的數(shù)據(jù)結(jié)構(gòu)(比如上面的類層次),而有著不同的行為,那么訪問者模式也許是個不錯的選擇。 |
溫馨提示:喜歡本站的話,請收藏一下本站!