訪問者模式的目的是封裝一些施加於某種數據結構元素之上的操做。一旦這些操做須要修改的話,接受這個操做的數據結構則能夠保持不變。java
訪問者模式適用於數據結構相對穩定的系統,它把數據結構和做用於結構上的操做之間的耦合解脫開。node
數據結構的每個節點均可以接受一個訪問者的調用,此節點向訪問者對象傳入節點對象,而訪問者對象反過來執行節點對象的操做。這樣的過程叫作"雙重分派"。節點調用訪問者,將它本身傳入,訪問者則將某算法針對此節點執行。算法
類圖以下:數據結構
涉及的角色以下:ide
抽象訪問者(Visitor)角色:聲明瞭一個或者多個方法操做,造成全部的具體訪問者角色必須實現的接口。
具體訪問者(ConcreteVisitor)角色:實現抽象訪問者所聲明的接口,也就是抽象訪問者所聲明的各個訪問操做。
抽象節點(Node)角色:聲明一個接受操做,接受一個訪問者對象做爲一個參數。
具體節點(ConcreteNode)角色:實現了抽象節點所規定的接受操做。
結構對象(ObjectStructure)角色:有以下的責任,能夠遍歷結構中的全部元素;若是須要,提供一個高層次的接口讓訪問者對象能夠訪問每個元素;若是須要,能夠設計成一個複合對象或者一個彙集,如List或Set。this
代碼以下:spa
抽象訪問者角色:爲每個具體節點都準備了一個訪問操做。因爲有兩個節點,所以,對應就有兩個訪問操做。設計
package visitor; public interface Visitor { void visit(NodeA node); void visit(NodeB node); }
package visitor; public class VisitorA implements Visitor { /** * 訪問A操做 */ @Override public void visit(NodeA node) { String operationA = node.operationA(); System.out.println(operationA); } /** * 訪問B操做 */ @Override public void visit(NodeB node) { String operationB = node.operationB(); System.out.println(operationB); } }
package visitor; public class VisitorB implements Visitor { /** * 訪問A操做 */ @Override public void visit(NodeA node) { String operationA = node.operationA(); System.out.println(operationA); } /** * 訪問B操做 */ @Override public void visit(NodeB node) { String operationB = node.operationB(); System.out.println(operationB); } }
抽象節點類:聲明一個接受操做3d
package visitor; public abstract class Node { public abstract void accept(Visitor visitor); }
具體節點:除了接受接受方法,還有其餘若干個商業方法,好比:operationA\operationBcode
package visitor; public class NodeA extends Node { /** * 接受操做 */ @Override public void accept(Visitor visitor) { visitor.visit(this); } /** * NodeA特有的方法 */ public String operationA() { return "NodeA operationA"; } }
package visitor; public class NodeB extends Node { /** * 接受操做 */ @Override public void accept(Visitor visitor) { visitor.visit(this); } /** * NodeA特有的方法 */ public String operationB() { return "NodeB operationB"; } }
結構對象角色類,這個結構對象角色持有一個彙集,並向外界提供add()方法做爲對彙集的管理操做。
package visitor; import java.util.ArrayList; import java.util.List; public class ObjectStructure { private List<Node> nodes = new ArrayList<Node>(); /** * 執行方法操做 */ public void action(Visitor visitor) { for (Node node : nodes) { node.accept(visitor); } } /** * 添加一個新元素 */ public void add(Node node) { nodes.add(node); } }
客戶端代碼:
package visitor; public class Client { private static ObjectStructure objectStructure; private static Visitor visitor; public static void main(String[] args) { objectStructure = new ObjectStructure(); // 給結構增長一個節點 objectStructure.add(new NodeA()); // 給結構增長一個節點 objectStructure.add(new NodeB()); // 建立一個訪問者 visitor = new VisitorA(); objectStructure.action(visitor); } }
結果:
NodeA operationA
NodeB operationB
雖然在這個示意性的實現裏並無出現一個複雜的具備多個樹枝節點的對象樹結構,可是,在實際系統中訪問者模式一般是用來處理複雜的對象樹結構的,並且訪問者模式能夠用來處理跨越多個等級結構的樹結構問題。這正是訪問者模式的功能強大之處。
一個計算機的訪問者模式的例子。建立一個定義接受操做的 ComputerPart 接口。Keyboard、Mouse、Monitor 和 Computer 是實現了 ComputerPart 接口的實體類。咱們將定義另外一個接口 ComputerPartVisitor,它定義了訪問者類的操做。Computer 使用實體訪問者來執行相應的動做。
類圖:
源碼以下:
(1)表示元素的接口
package visitor1; public interface ComputerPart { public void accept(ComputerPartVisitor computerPartVisitor); }
(2)擴展了上述類的實體類。
package visitor1; public class Keyboard implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } }
package visitor1; public class Monitor implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } }
package visitor1; public class Mouse implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } }
至關於結構對象角色:
package visitor1; public class Computer implements ComputerPart { private ComputerPart[] parts; public Computer() { parts = new ComputerPart[] { new Mouse(), new Keyboard(), new Monitor() }; } @Override public void accept(ComputerPartVisitor computerPartVisitor) { for (int i = 0; i < parts.length; i++) { parts[i].accept(computerPartVisitor); } computerPartVisitor.visit(this); } }
(3)定義一個表示訪問者的接口。
package visitor1; public interface ComputerPartVisitor { public void visit(Computer computer); public void visit(Mouse mouse); public void visit(Keyboard keyboard); public void visit(Monitor monitor); }
(4)實現了上述類的實體訪問者。
package visitor1; public class ComputerPartDisplayVisitor implements ComputerPartVisitor { @Override public void visit(Computer computer) { System.out.println("Displaying Computer."); } @Override public void visit(Mouse mouse) { System.out.println("Displaying Mouse."); } @Override public void visit(Keyboard keyboard) { System.out.println("Displaying Keyboard."); } @Override public void visit(Monitor monitor) { System.out.println("Displaying Monitor."); } }
(5)客戶端代碼:
package visitor1; public class Client { public static void main(String[] args) { ComputerPart computer = new Computer(); computer.accept(new ComputerPartDisplayVisitor()); } }
結果:
Displaying Mouse.
Displaying Keyboard.
Displaying Monitor.
Displaying Computer.
意圖:主要將數據結構與數據操做分離。
主要解決:穩定的數據結構和易變的操做耦合問題。
什麼時候使用:須要對一個對象結構中的對象進行不少不一樣的而且不相關的操做,而須要避免讓這些操做"污染"這些對象的類,使用訪問者模式將這些封裝到類中。
如何解決:在被訪問的類裏面加一個對外提供接待訪問者的接口。
關鍵代碼:在數據基礎類裏面有一個方法接受訪問者,將自身引用傳入訪問者。
應用實例:您在朋友家作客,您是訪問者,朋友接受您的訪問,您經過朋友的描述,而後對朋友的描述作出一個判斷,這就是訪問者模式。
優勢: 一、符合單一職責原則。 二、優秀的擴展性。 三、靈活性。
缺點: 一、具體元素對訪問者公佈細節,違反了迪米特原則。 二、具體元素變動比較困難。 三、違反了依賴倒置原則,依賴了具體類,沒有依賴抽象。
使用場景: 一、對象結構中對象對應的類不多改變,但常常須要在此對象結構上定義新的操做。 二、須要對一個對象結構中的對象進行不少不一樣的而且不相關的操做,而須要避免讓這些操做"污染"這些對象的類,也不但願在增長新操做時修改這些類。
注意事項:訪問者能夠對功能進行統一,能夠作報表、UI、攔截器與過濾器。