本文可能是你看過的最易懂的訪問者設計模式的文章之一,以示例為基礎,不斷演化,深入訪問者模式的內核,給出了訪問者模式的意圖,結構,示例代碼。 ...
訪問者模式 Visitor
![image_5c2481f3_6073 image_5c2481f3_6073](https://img2018.cnblogs.com/blog/897393/201812/897393-20181227154314702-1288896911.png)
意圖
表示一個作用於某對象結構中的各元素的操作。 它使你可以在不改變各元素類的前提下定義作用於這些元素的新操作。意圖解析
我們以代碼描述一下《俠客行》中的這個場景 假定: 賞善罰惡二使,一個叫做張三,一個叫做李四,面對一眾掌門 張三負責賞善,對好人賞賜,壞人他不處理; 相反,李四負責罰惡,好人不處理,對壞人懲罰俠客行代碼示例
定義了“掌門人”介面package visitor.俠客行; public interface 掌門人 { }“掌門人”有兩種類型 沒做過壞事的掌門,做過壞事的掌門
package visitor.俠客行; public class 沒做過壞事的掌門 implements 掌門人 { }
package visitor.俠客行; public class 做過壞事的掌門 implements 掌門人 { }定義了俠客島,俠客島管理維護“江湖的掌門人”,使用List 提供了掌門人的添加方法 “add掌門人(掌門人 某掌門)” 定義了“賞善罰惡(String 處理人)”方法,用於賞善罰惡,接受參數為處理人 如果是賞善大使張三,他會賞賜好人,不管壞人 如果是罰惡大使李四,他會懲罰壞人,不管好人
package visitor.俠客行; import java.util.ArrayList; import java.util.List; public class 俠客島 { private List<掌門人> 掌門人List = new ArrayList<>(); public void add掌門人(掌門人 某掌門) { 掌門人List.add(某掌門); } public void 賞善罰惡(String 處理人) { if (處理人.equals("張三")) { for (掌門人 某掌門X : 掌門人List) { if (某掌門X instanceof 沒做過壞事的掌門) { System.out.println("好掌門, 張三: 賞賜沒做過壞事的掌門"); } else if (某掌門X instanceof 做過壞事的掌門) { System.out.println("壞掌門, 張三: 不管做過壞事的掌門"); } System.out.println(); } } else if (處理人.equals("李四")) { for (掌門人 某掌門X : 掌門人List) { if (某掌門X instanceof 沒做過壞事的掌門) { System.out.println("好掌門, 李四: 不管沒做過壞事的掌門"); } else if (某掌門X instanceof 做過壞事的掌門) { System.out.println("壞掌門, 李四: 懲罰做過壞事的掌門"); } System.out.println(); } } } }
測試代碼
![image_5c2481f3_37bc image_5c2481f3_37bc](https://img2018.cnblogs.com/blog/897393/201812/897393-20181227154316070-1705517240.png)
![image_5c2481f3_7af6 image_5c2481f3_7af6](https://img2018.cnblogs.com/blog/897393/201812/897393-20181227154316755-405446482.png)
新版代碼示例
掌門人相關角色不變package visitor.新版俠客行; public interface 掌門人 { } package visitor.新版俠客行; public class 沒做過壞事的掌門 implements 掌門人 { } package visitor.新版俠客行; public class 做過壞事的掌門 implements 掌門人 { }新增加訪問者角色,訪問者既可能訪問好人,也可能訪問壞人,使用方法的重載在解決 方法都是拜訪,有兩種類型的重載版本
package visitor.新版俠客行; public interface 訪問使者 { void 拜訪(做過壞事的掌門 壞人); void 拜訪(沒做過壞事的掌門 好人); }張三負責賞善,當他訪問到好人時,賞賜,壞人不處理
package visitor.新版俠客行; public class 張三 implements 訪問使者 { @Override public void 拜訪(沒做過壞事的掌門 好人) { System.out.println("好掌門, 張三: 賞賜沒做過壞事的掌門"); } @Override public void 拜訪(做過壞事的掌門 壞人) { System.out.println("壞掌門, 張三: 不管做過壞事的掌門"); } }李四負責罰惡,訪問到好人時不處理,遇到壞人時,就懲罰!
package visitor.新版俠客行; public class 李四 implements 訪問使者 { @Override public void 拜訪(沒做過壞事的掌門 好人) { System.out.println("好掌門, 李四: 不管沒做過壞事的掌門"); } @Override public void 拜訪(做過壞事的掌門 壞人) { System.out.println("壞掌門, 李四: 懲罰做過壞事的掌門"); } }引入了訪問使者角色,我們就不需要對使者進行判斷了 藉助了使者的多態性,不管是何種使者都有訪問不同類型掌門人的方法 所以可以去掉了一層邏輯判斷,代碼簡化如下
package visitor.新版俠客行; import java.util.ArrayList; import java.util.List; public class 俠客島 { private List<掌門人> 掌門人List = new ArrayList<>(); public void add掌門人(掌門人 某掌門) { 掌門人List.add(某掌門); } public void 賞善罰惡(訪問使者 使者) { for (掌門人 某掌門X : 掌門人List) { if (某掌門X instanceof 沒做過壞事的掌門) { 使者.拜訪((沒做過壞事的掌門)某掌門X); } else if (某掌門X instanceof 做過壞事的掌門) { 使者.拜訪((做過壞事的掌門)某掌門X); } System.out.println(); } } }測試代碼也稍作調整 定義了兩個訪問者,傳遞給“賞善罰惡”方法
package visitor.新版俠客行; public class Test { public static void main(String[] args){ 俠客島 善善罰惡二使 = new 俠客島(); 善善罰惡二使.add掌門人(new 做過壞事的掌門()); 善善罰惡二使.add掌門人(new 沒做過壞事的掌門()); 善善罰惡二使.add掌門人(new 沒做過壞事的掌門()); 善善罰惡二使.add掌門人(new 做過壞事的掌門()); 訪問使者 張三 = new 張三(); 訪問使者 李四 = new 李四(); 善善罰惡二使.賞善罰惡(李四); 善善罰惡二使.賞善罰惡(張三); } }
![image_5c2481f3_6b9 image_5c2481f3_6b9](https://img2018.cnblogs.com/blog/897393/201812/897393-20181227154317136-1001037108.png)
package visitor.新版俠客行; public class 龍木島主 implements 訪問使者 { @Override public void 拜訪(做過壞事的掌門 壞人) { System.out.println("龍木島主,懲罰壞人"); } @Override public void 拜訪(沒做過壞事的掌門 好人) { System.out.println("龍木島主,賞賜好人"); } }新增加了"龍木島主“訪客後,客戶端可以直接使用了,不需要修改”俠客島“的代碼了 測試代碼增加如下兩行,查看下麵結果
![image_5c2481f3_110a image_5c2481f3_110a](https://img2018.cnblogs.com/blog/897393/201812/897393-20181227154317503-1125817037.png)
![image_5c2481f3_1f7e image_5c2481f3_1f7e](https://img2018.cnblogs.com/blog/897393/201812/897393-20181227154317914-1090090671.png)
![image_5c2481f3_959 image_5c2481f3_959](https://img2018.cnblogs.com/blog/897393/201812/897393-20181227154318295-1546539562.png)
![image_5c2481f4_73d5 image_5c2481f4_73d5](https://img2018.cnblogs.com/blog/897393/201812/897393-20181227154319129-972217722.png)
最新版俠客行代碼示例
說起來有點迷惑,我看看代碼 《最新版俠客行》 掌門人都增加了”接受拜訪“的方法package visitor.最新版本俠客行; public interface 掌門人 { void 接受拜訪(訪問使者 賞善使者); }
package visitor.最新版本俠客行; public class 沒做過壞事的掌門 implements 掌門人 { @Override public void 接受拜訪(訪問使者 賞善罰惡使者) { 賞善罰惡使者.拜訪(this); } }
package visitor.最新版本俠客行; public class 做過壞事的掌門 implements 掌門人 { @Override public void 接受拜訪(訪問使者 賞善罰惡使者) { 賞善罰惡使者.拜訪(this); } }訪問使者相關角色與《新版俠客行》中一樣
package visitor.最新版本俠客行; public interface 訪問使者 { void 拜訪(做過壞事的掌門 壞人); void 拜訪(沒做過壞事的掌門 好人); } package visitor.最新版本俠客行; public class 張三 implements 訪問使者 { @Override public void 拜訪(沒做過壞事的掌門 好人) { System.out.println("好掌門, 張三: 賞賜沒做過壞事的掌門"); } @Override public void 拜訪(做過壞事的掌門 壞人) { System.out.println("壞掌門, 張三: 不管做過壞事的掌門"); } }此時的俠客島輕鬆了,不再需要來回的判斷類型了
package visitor.最新版本俠客行; public class 李四 implements 訪問使者 { @Override public void 拜訪(沒做過壞事的掌門 好人) { System.out.println("好掌門, 李四: 不管沒做過壞事的掌門"); } @Override public void 拜訪(做過壞事的掌門 壞人) { System.out.println("壞掌門, 李四: 懲罰做過壞事的掌門"); } }
package visitor.最新版本俠客行; import java.util.ArrayList; import java.util.List; public class 俠客島 { private List<掌門人> 掌門人List = new ArrayList<>(); public void add掌門人(掌門人 某掌門) { 掌門人List.add(某掌門); } public void 賞善罰惡(訪問使者 使者) { for (掌門人 某掌門X : 掌門人List) { 某掌門X.接受拜訪(使者); System.out.println(); } } }
![image_5c2481f4_4529 image_5c2481f4_4529](https://img2018.cnblogs.com/blog/897393/201812/897393-20181227154319786-474236469.png)
package visitor.最新版本俠客行; public class 龍木島主 implements 訪問使者 { @Override public void 拜訪(做過壞事的掌門 壞人) { System.out.println("龍木島主,懲罰壞人"); } @Override public void 拜訪(沒做過壞事的掌門 好人) { System.out.println("龍木島主,賞賜好人"); } }測試代碼如下,顯然因為拜訪使者的抽象,才得以能夠更好的擴展訪問者,所以此處肯定跟《新版俠客行》一樣便於擴展
![image_5c2481f4_4404 image_5c2481f4_4404](https://img2018.cnblogs.com/blog/897393/201812/897393-20181227154320257-1399416512.png)
package visitor.最新版本俠客行; public class 不好不壞的掌門 implements 掌門人 { @Override public void 接受拜訪(訪問使者 賞善罰惡使者) { 賞善罰惡使者.拜訪(this); } }但是,”訪問使者“裡面沒有能夠拜訪”不好不壞的掌門“方法啊?怎麼辦? 只能添加唄,如下圖所示,完蛋了........
![image_5c2481f4_3f58 image_5c2481f4_3f58](https://img2018.cnblogs.com/blog/897393/201812/897393-20181227154320783-771913568.png)
代碼演化小結
看得出來,《最新版俠客行》解決了複雜判斷的問題,也解決了訪問者擴展的問題 但是對於被訪問者的類型的擴展,顯然是沒有擴展性的,不符合開閉原則 這一點體現出來了這種解決方法的傾向性,傾向於擴展行為,可以自如的增加新的行為 但是不能輕鬆的增加元素類型 測試代碼Test類不需要修改 看一下列印結果![image_5c2481f4_3462 image_5c2481f4_3462](https://img2018.cnblogs.com/blog/897393/201812/897393-20181227154321516-610225769.png)
最新版俠客行結構
![image_5c2481f4_7425 image_5c2481f4_7425](https://img2018.cnblogs.com/blog/897393/201812/897393-20181227154322139-105580929.png)
迴首意圖
再回頭看下訪問者模式的意圖 表示一個作用於某對象結構中的各元素的操作。它使你可以在不改變各元素類的前提下定義作用於這些元素的新操作。 就是上面示例中,對於來訪者的擴展嘛 最初的動機就是處理《俠客行》中類似的問題 集合容器中保存了不同類型的對象,他們又可能有多種不同場景的操作 比如一份名單,班長可能拿過去收作業,班主任拿過去可能點名 名單裡面都有你也有他,你就是那個你,他還是那個他,但是你的作業是你的作業,他的作業是他的作業。 所以對於班長和班主任兩個訪問者,同學們的行為是不一樣的,對同一來訪者,不同的同學的行為又是不一樣的結構
![image_5c2481f4_1c63 image_5c2481f4_1c63](https://img2018.cnblogs.com/blog/897393/201812/897393-20181227154322548-2135975363.png)
概念示例代碼
我們可以抽象化的看下下麵的例子 下麵的代碼很簡單,A有三種子類型,B有三種子類型 不同的A和不同的B,將會擦出不一樣的火花,也就是會出現9種可能的場景 將A定義為訪問者,那麼A就要藉助方法的重載實現不同類型被訪問者B的不同行為 而將方法的調用轉變為被訪問者的反向調用----this傳遞給訪問者package visitor; public class example { public static void main(String[] args) { A1 a1 = new A1(); A2 a2 = new A2(); A3 a3 = new A3(); B1 b1 = new B1(); B2 b2 = new B2(); B3 b3 = new B3(); b1.accept(a1); b1.accept(a2); b1.accept(a3); b2.accept(a1); b2.accept(a2); b2.accept(a3); b3.accept(a1); b3.accept(a2); b3.accept(a3); } } abstract class A { abstract void visit(B1 b1); abstract void visit(B2 b2); abstract void visit(B3 b3); } class A1 extends A { @Override void visit(B1 b1) { System.out.println("A1 play with B1"); } @Override void visit(B2 b2) { System.out.println("A1 play with B2"); } @Override void visit(B3 b3) { System.out.println("A1 play with B3"); } } class A2 extends A { @Override void visit(B1 b1) { System.out.println("A2 play with B1"); } @Override void visit(B2 b2) { System.out.println("A2 play with B2"); } @Override void visit(B3 b3) { System.out.println("A2 play with B3"); } } class A3 extends A { @Override void visit(B1 b1) { System.out.println("A3 play with B1"); } @Override void visit(B2 b2) { System.out.println("A3 play with B2"); } @Override void visit(B3 b3) { System.out.println("A3 play with B3"); } } abstract class B { abstract void accept(A a); } class B1 extends B { @Override void accept(A a) { a.visit(this); } } class B2 extends B { @Override void accept(A a) { a.visit(this); } } class B3 extends B { @Override void accept(A a) { a.visit(this); } }
![image_5c2481f4_6086 image_5c2481f4_6086](https://img2018.cnblogs.com/blog/897393/201812/897393-20181227154322953-831462540.png)