訪問者模式(Visitor)


訪問者模式(Visitor

 

訪問者模式(Visitor

意圖:表示一個作用於某對象結構中的各元素的操作,它使你在不改變各元素的類的前提下定義作用於這些元素的新操作。

應用:作用於編譯器語法樹的語義分析算法。

模式結構

心得

訪問者模式是要解決對對象添加新的操作和功能時候,如何盡可能不修改對象的類的一種方法。一般為對象添加功能,是需要向對象添加成員函數。但這里對對象(ConcreteElement)添加了一個統一的接口——accept,來接收一個訪問者對象。如何把對對象的操作移出到類外,正是接收參數(Visitor)的作用。它通過調用Visitor的接口函數visitConcreteElement針對當前對象進行操作,當然,當前對象的指針需要被作為參數傳遞出去,以便對對象狀態進行訪問。這樣,擁有Element集合的對象ObjectStruct只要通過遍歷操作,每次調用對象的accept接口就可以讓對象自動告訴訪問者使用執行什么樣的功能了。當需要為對象擴展功能時,只需要再添加一個訪問者,重定義對每類對象進行訪問的方式就可以了。這里涉及一個雙向分派的概念,即accept操作的調用者(Element)是運行時多態的,而且參數Visitor也是運行時多態的。正是因為如此,才讓用戶可以通過將新添的功能封裝為對象,來實現對對象集合批量的不同操作。

舉例

這里其實可以把Element想象為編譯器的抽象語法樹節點,ConcreteElement可以看作具體的樹節點,如賦值語句和變量訪問節點。Visitor就可以看作語義分析階段的語義檢查,ConcreteVistor可以看作類型檢查功能和代碼生成功能。這些語義分析的功能顯然不應該和語法樹放在一起,那么把它封裝為訪問者,讓他們為不同的節點生成單獨的分析流程和算法。再在節點對象內部使用統一接口accept調用對應的算法即可,節點內容通過自身的對象指針傳遞給訪問者對象。按照圖中所示關系,我們給出C++代碼如下:

// 元素基類
class Visitor;
class Element
{
public:
     virtual  void accept(Visitor*)= 0;
     virtual ~Element(){}
};
// 訪問者基類
class ConcreteElementA;
class ConcreteElementB;
class Visitor
{
public:
     virtual  void visitConcreteElementA(ConcreteElementA*)= 0;
     virtual  void visitConcreteElementB(ConcreteElementB*)= 0;
     virtual ~Visitor(){}
};
// 具體元素
class ConcreteElementA: public Element
{
public:
     virtual  void accept(Visitor*v)
    {
        v->visitConcreteElementA( this); // 雙向分派
    }
     void operationA()
    {
        cout<< " 對元素A的處理 "<<endl;
    }
};
class ConcreteElementB: public Element
{
public:
     virtual  void accept(Visitor*v)
    {
        v->visitConcreteElementB( this);
    }
     void operationB()
    {
        cout<< " 對元素B的處理 "<<endl;
    }
};
// 具體的訪問者
class ConcreteVisitor1: public Visitor
{
public:
     virtual  void visitConcreteElementA(ConcreteElementA*ea)
    {
        cout<< " 訪問者1 ";
        ea->operationA();
    }
     virtual  void visitConcreteElementB(ConcreteElementB*eb)
    {
        cout<< " 訪問者1 ";
        eb->operationB();
    }
};
class ConcreteVisitor2: public Visitor
{
public:
     virtual  void visitConcreteElementA(ConcreteElementA*ea)
    {
        cout<< " 訪問者2 ";
        ea->operationA();
    }
     virtual  void visitConcreteElementB(ConcreteElementB*eb)
    {
        cout<< " 訪問者2 ";
        eb->operationB();
    }
};
// 管理和遍歷元素集合的高層類
class ObjectStruct
{
    list<Element*>data;
public:
     void addElement(Element*e)
    {
        data.push_back(e);
    }
     void delElement(Element*e)
    {
        data.remove(e);
    }
     void dispaly(Visitor*v)
    {
         for(list<Element*>::iterator it=data.begin();
            it!=data.end();++it)
        {
            (*it)->accept(v);
        }
    }
    ~ObjectStruct()
    {
         for(list<Element*>::iterator it=data.begin();
            it!=data.end();++it)
        {
            delete (*it);
        }
    }
};

這里需要實現一下ObjectStruct類,因為它提供的對象集合的高層遍歷。用戶對元素的逐個操作被簡化為如下方式:

ObjectStruct os; // 初始化集合
os.addElement( new ConcreteElementA());
os.addElement( new ConcreteElementB());
os.addElement( new ConcreteElementB());
os.addElement( new ConcreteElementA());
Visitor*v1= new ConcreteVisitor1(); // 創建訪問者1
Visitor*v2= new ConcreteVisitor2();
os.dispaly(v1); // 用訪問者1對元素進行操作【雙向分派】
os.dispaly(v2);

由此看來,只要對象的繼承結構(數據結構)變化不大的情況下,比如不會添加新的類型的節點,使用Visitor模式是非常合適的。用戶只要按需創建合適的訪問者類實現之,然后遍歷集合對象,直接“訪問”就可以了。額外需要說明的一點是,訪問者並不一定讓具體元素類繼承於統一的父類,從訪問者抽象類也能看出,抽象類接口僅僅依賴於具體實現的類。之所以讓它們具有公共的基類主要是還是為了批量操作的方便,即使沒有繼承統一的基類,訪問者模式依然能工作,也能為具體的類添加功能。

參考文章http://blog.sina.com.cn/s/blog_8da636240100uurx.html


免責聲明!

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



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