設計模式之訪問者模式(Visitor)詳解及代碼示例


一、訪問者模式的定義與特點

  訪問者(Visitor)模式的定義:將作用於某種數據結構中的各元素的操作分離出來封裝成獨立的類,使其在不改變數據結構的前提下可以添加作用於這些元素的新的操作,為數據結構中的每個元素提供多種訪問方式。它將對數據的操作與數據結構進行分離,是行為類模式中最復雜的一種模式。

二、訪問者模式優缺點

  訪問者(Visitor)模式是一種對象行為型模式,其主要優點如下:

  • 擴展性好。能夠在不修改對象結構中的元素的情況下,為對象結構中的元素添加新的功能。
  • 復用性好。可以通過訪問者來定義整個對象結構通用的功能,從而提高系統的復用程度。
  • 靈活性好。訪問者模式將數據結構與作用於結構上的操作解耦,使得操作集合可相對自由地演化而不影響系統的數據結構。
  • 符合單一職責原則。訪問者模式把相關的行為封裝在一起,構成一個訪問者,使每一個訪問者的功能都比較單一。

  訪問者(Visitor)模式的主要缺點如下:

  • 增加新的元素類很困難。在訪問者模式中,每增加一個新的元素類,都要在每一個具體訪問者類中增加相應的具體操作,這違背了“開閉原則”。
  • 破壞封裝。訪問者模式中具體元素對訪問者公布細節,這破壞了對象的封裝性。
  • 違反了依賴倒置原則。訪問者模式依賴了具體類,而沒有依賴抽象類。

三、訪問者模式的實現

  訪問者(Visitor)模式實現的關鍵是如何將作用於元素的操作分離出來封裝成獨立的類,訪問者模式包含以下主要角色:

  • 抽象訪問者(Visitor)角色:定義一個訪問具體元素的接口,為每個具體元素類對應一個訪問操作 visit() ,該操作中的參數類型標識了被訪問的具體元素。
  • 具體訪問者(ConcreteVisitor)角色:實現抽象訪問者角色中聲明的各個訪問操作,確定訪問者訪問一個元素時該做什么。
  • 抽象元素(Element)角色:聲明一個包含接受操作 accept() 的接口,被接受的訪問者對象作為 accept() 方法的參數。
  • 具體元素(ConcreteElement)角色:實現抽象元素角色提供的 accept() 操作,其方法體通常都是 visitor.visit(this) ,另外具體元素中可能還包含本身業務邏輯的相關操作。
  • 對象結構(Object Structure)角色:是一個包含元素角色的容器,提供讓訪問者對象遍歷容器中的所有元素的方法,通常由 List、Set、Map 等聚合類實現。

  其結構圖如圖所示:

          

   代碼如下:

public class VisitorPattern
{
    public static void main(String[] args)
    {
        ObjectStructure os=new ObjectStructure();
        os.add(new ConcreteElementA());
        os.add(new ConcreteElementB());
        Visitor visitor=new ConcreteVisitorA();
        os.accept(visitor);
        System.out.println("------------------------");
        visitor=new ConcreteVisitorB();
        os.accept(visitor);
    }
}
//抽象訪問者
interface Visitor
{
    void visit(ConcreteElementA element);
    void visit(ConcreteElementB element);
}
//具體訪問者A類
class ConcreteVisitorA implements Visitor
{
    public void visit(ConcreteElementA element)
    {
        System.out.println("具體訪問者A訪問-->"+element.operationA());
    }
    public void visit(ConcreteElementB element)
    {
        System.out.println("具體訪問者A訪問-->"+element.operationB());
    }
}
//具體訪問者B類
class ConcreteVisitorB implements Visitor
{
    public void visit(ConcreteElementA element)
    {
        System.out.println("具體訪問者B訪問-->"+element.operationA());
    }
    public void visit(ConcreteElementB element)
    {
        System.out.println("具體訪問者B訪問-->"+element.operationB());
    }
}
//抽象元素類
interface Element
{
    void accept(Visitor visitor);
}
//具體元素A類
class ConcreteElementA implements Element
{
    public void accept(Visitor visitor)
    {
        visitor.visit(this);
    }
    public String operationA()
    {
        return "具體元素A的操作。";
    }
}
//具體元素B類
class ConcreteElementB implements Element
{
    public void accept(Visitor visitor)
    {
        visitor.visit(this);
    }
    public String operationB()
    {
        return "具體元素B的操作。";
    }
}
//對象結構角色
class ObjectStructure
{   
    private List<Element> list=new ArrayList<Element>();   
    public void accept(Visitor visitor)
    {
        Iterator<Element> i=list.iterator();
        while(i.hasNext())
        {
            ((Element) i.next()).accept(visitor);
        }      
    }
    public void add(Element element)
    {
        list.add(element);
    }
    public void remove(Element element)
    {
        list.remove(element);
    }
}

  測試結果如下:

具體訪問者A訪問-->具體元素A的操作。
具體訪問者A訪問-->具體元素B的操作。
------------------------
具體訪問者B訪問-->具體元素A的操作。
具體訪問者B訪問-->具體元素B的操作。

  可以看到整個流程:

  • 首先組裝對象結構以及對象結構所包含的元素;
  • 接受訪問者訪問;
  • 對象結構根據注冊的元素進行遍歷,然后根據取出的元素、訪問者類型調用對應訪問者對應的操作;
  • 訪問者調用對應元素對應方法;
  • 循環所有元素后結束訪問。

四、訪問者模式的應用場景

  通常在以下情況可以考慮使用訪問者(Visitor)模式:

  • 對象結構相對穩定,但其操作算法經常變化的程序。
  • 對象結構中的對象需要提供多種不同且不相關的操作,而且要避免讓這些操作的變化影響對象的結構。
  • 對象結構包含很多類型的對象,希望對這些對象實施一些依賴於其具體類型的操作。

五、訪問者模式的擴展

  訪問者(Visitor)模式是使用頻率較高的一種設計模式,它常常同以下兩種設計模式聯用。

  • 與“迭代器模式”聯用。因為訪問者模式中的“對象結構”是一個包含元素角色的容器,當訪問者遍歷容器中的所有元素時,常常要用迭代器。如上面示例代碼中的對象結構是用 List 實現的,它通過 List 對象的 Iterator() 方法獲取迭代器。如果對象結構中的聚合類沒有提供迭代器,也可以用迭代器模式自定義一個。
  • 訪問者(Visitor)模式同“組合模式”聯用。因為訪問者(Visitor)模式中的“元素對象”可能是葉子對象或者是容器對象,如果元素對象包含容器對象,就必須用到組合模式,其結構圖如圖所示:

            


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM