定義
封裝某些做用於某種數據結構中各元素的操做,它能夠在不改變數據結構的前提下定義做用於這些元素的新的操做。java
class A { public void method1(){ System.out.println("我是A"); } public void method2(B b){ b.showA(this); } } class B { public void showA(A a){ a.method1(); } }
看一下在類A中,方法method1和方法method2的區別在哪裏,方法method1很簡單,就是打印出一句「我是A」;方法method2稍微複雜一點,使用類B做爲參數,並調用類B的showA方法。再來看一下類B的showA方法,showA方法使用類A做爲參數,而後調用類A的method1方法,能夠看到,method2方法繞來繞去,無非就是調用了一下本身的method1方法而已,它的運行結果應該也是「我是A」,分析完以後,咱們來運行一下這兩個方法,並看一下運行結果:數據結構
public class Test { public static void main(String[] args){ A a = new A(); a.method1(); a.method2(new B()); } }
運行結果爲:dom
我是A
我是Athis
角色
在例子中,對於類A來講,類B就是一個訪問者。
抽象訪問者:抽象類或者接口,聲明訪問者能夠訪問哪些元素,具體到程序中就是visit方法中的參數定義哪些對象是能夠被訪問的。
訪問者:實現抽象訪問者所聲明的方法,它影響到訪問者訪問到一個類後該幹什麼,要作什麼事情。
抽象元素類:接口或者抽象類,聲明接受哪一類訪問者訪問,程序上是經過accept方法中的參數來定義的。抽象元素通常有兩類方法,一部分是自己的業務邏輯,另外就是容許接收哪類訪問者來訪問。
元素類:實現抽象元素類所聲明的accept方法,一般都是visitor.visit(this),基本上已經造成一種定式了。
結構對象:一個元素的容器,通常包含一個容納多個不一樣類、不一樣接口的容器,如List、Set、Map等,在項目中通常不多抽象出這個角色。spa
抽象元素類code
abstract class Element { public abstract void accept(IVisitor visitor); public abstract void doSomething(); }
元素類對象
class ConcreteElement1 extends Element { public void doSomething(){ System.out.println("這是元素1"); } public void accept(IVisitor visitor) { visitor.visit(this); } } class ConcreteElement2 extends Element { public void doSomething(){ System.out.println("這是元素2"); } public void accept(IVisitor visitor) { visitor.visit(this); } }
抽象訪問者接口
interface IVisitor { public void visit(ConcreteElement1 el1); public void visit(ConcreteElement2 el2); }
訪問者get
class Visitor implements IVisitor { public void visit(ConcreteElement1 el1) { el1.doSomething(); } public void visit(ConcreteElement2 el2) { el2.doSomething(); } }
結構對象it
class ObjectStruture { public static List<Element> getList(){ List<Element> list = new ArrayList<Element>(); Random ran = new Random(); for(int i=0; i<10; i++){ int a = ran.nextInt(100); if(a>50){ list.add(new ConcreteElement1()); }else{ list.add(new ConcreteElement2()); } } return list; } }
客戶端
public class Client { public static void main(String[] args){ List<Element> list = ObjectStruture.getList(); for(Element e: list){ e.accept(new Visitor()); } } }
優勢
符合單一職責原則:凡是適用訪問者模式的場景中,元素類中須要封裝在訪問者中的操做一定是與元素類自己關係不大且是易變的操做,使用訪問者模式一方面符合單一職責原則,另外一方面,由於被封裝的操做一般來講都是易變的,因此當發生變化時,就能夠在不改變元素類自己的前提下,實現對變化部分的擴展。
擴展性良好:元素類能夠經過接受不一樣的訪問者來實現對不一樣操做的擴展。
缺點
增長新的元素類比較困難。經過訪問者模式的代碼能夠看到,在訪問者類中,每個元素類都有它對應的處理方法,也就是說,每增長一個元素類都須要修改訪問者類(也包括訪問者類的子類或者實現類),修改起來至關麻煩。也就是說,在元素類數目不肯定的狀況下,應該慎用訪問者模式。因此,訪問者模式比較適用於對已有功能的重構,好比說,一個項目的基本功能已經肯定下來,元素類的數據已經基本肯定下來不會變了,會變的只是這些元素內的相關操做,這時候,咱們可使用訪問者模式對原有的代碼進行重構一遍,這樣一來,就能夠在不修改各個元素類的狀況下,對原有功能進行修改。
適用場景 假如一個對象中存在着一些與本對象不相干(或者關係較弱)的操做,爲了不這些操做污染這個對象,則可使用訪問者模式來把這些操做封裝到訪問者中去。 假如一組對象中,存在着類似的操做,爲了不出現大量重複的代碼,也能夠將這些重複的操做封裝到訪問者中去。