意圖:
表示一個作用於某對象結構的各元素的操作。它使你可以再不改變各元素的類的前提下定義作用於這些元素的新操作。
動機:
之前在學校的最后一個小項目就是做一個編譯器,當時使用的就是訪問者模式。
在靜態分析階段,將源程序表示為一個抽象語法樹,編譯器需要在抽象語法樹的基礎上實施某些操作以進行靜態語義分析。可能需要定義許多操作以進行類型檢查、代碼優化、流程分析、檢查變量是否在使用前被賦值,等等。
這個需求的特點是:要求對不同的節點進行不同的處理。
常規設計方法:不同的節點封裝不同的操作。
缺點是,節點類型過多,將操作分散在各個節點類中會導致整個系統難以理解、維護和修改。增加新的操作要修改和重新編譯所有的類。
改進:節點類獨立於作用於其上的操作。
1 將相關操作封裝在一個獨立的對象(Visitor)中,並在遍歷抽象語法樹時將此對象傳遞給當前訪問的元素。
2 當一個節點接受一個訪問者時,該元素向訪問者發送一個包含自身類信息的請求。該請求同時也將該元素本身作為一個參數。
3 訪問者將對該元素執行該操作。
適用性:
在下列情況下使用Visitor模式:
一個對象結構包含很多類對象
需要對其中的對象進行很多不同的並且不相關的操作
對象很少改變,經常需要對其上的操作進行修改或新增
需要注意的一點是,如果對象結果和接口經常改變,那么會導致需要重定義所有訪問者的接口,會導致很大的代價。所以這種情況還是在對象類中定義操作比較好。
結構:
協作:
示例代碼:
背景:假設你的電腦出現問題了 ,拿到售后那邊檢測,售后告訴你必須拆機檢測,檢測過程通過兩個技術人員依次負責不同功能的檢測。
分析:本例中,兩個負責不同功能檢測的技術人員就是Visitor,而電腦的各個部件就是elements。
特點:電腦的部件是固定的,不會有太大的改變,但是如果一種檢測方式沒有找出問題的話,那么就需要增加檢測項。符合訪問者模式的特點。
//visit.h
#ifndef VISITOR_H
#define VISITOR_H
#include <iostream>
#include <string>
#include <vector>
class Element;
class CPU;
class VideoCard;
class MainBoard;
/*------------------*/
class Visitor {
public:
Visitor(std::string name) {
visitorName = name;
}
virtual void visitCPU( CPU* cpu ) {};
virtual void visitVideoCard( VideoCard* videoCard ) {};
virtual void visitMainBoard( MainBoard* mainBoard ) {};
std::string getName() {
return this->visitorName;
};
private:
std::string visitorName;
};
class Element {
public:
Element( std::string name ) {
eleName = name;
}
virtual void accept( Visitor* visitor ) {};
virtual std::string getName() {
return this->eleName;
}
private:
std::string eleName;
};
/*----------- Elements -------------*/
class CPU : public Element {
public:
CPU(std::string name) : Element(name) {}
void accept(Visitor* visitor) {
visitor->visitCPU(this);
}
};
class VideoCard : public Element {
public:
VideoCard(std::string name) : Element(name) {}
void accept(Visitor* visitor) {
visitor->visitVideoCard(this);
}
};
class MainBoard : public Element {
public:
MainBoard(std::string name) : Element(name) {}
void accept(Visitor* visitor) {
visitor->visitMainBoard(this);
}
};
/*----------- ConcreteVisitor -------------*/
class CircuitDetector : public Visitor {
public:
CircuitDetector(std::string name) : Visitor(name) {}
// checking cpu
void visitCPU( CPU* cpu ) {
std::cout << Visitor::getName() << " is checking CPU's circuits.(" << cpu->getName()<<")" << std::endl;
}
// checking videoCard
void visitVideoCard( VideoCard* videoCard ) {
std::cout << Visitor::getName() << " is checking VideoCard's circuits.(" << videoCard->getName()<<")" << std::endl;
}
// checking mainboard
void visitMainBoard( MainBoard* mainboard ) {
std::cout << Visitor::getName() << " is checking MainBoard's circuits.(" << mainboard->getName() <<")" << std::endl;
}
};
class FunctionDetector : public Visitor {
public:
FunctionDetector(std::string name) : Visitor(name) {}
virtual void visitCPU( CPU* cpu ) {
std::cout << Visitor::getName() << " is check CPU's function.(" << cpu->getName() << ")"<< std::endl;
}
// checking videoCard
void visitVideoCard( VideoCard* videoCard ) {
std::cout << Visitor::getName() << " is checking VideoCard's function.(" << videoCard->getName()<< ")" << std::endl;
}
// checking mainboard
void visitMainBoard( MainBoard* mainboard ) {
std::cout << Visitor::getName() << " is checking MainBoard's function.(" << mainboard->getName() << ")"<< std::endl;
}
};
/*------------------------*/
class Computer {
public:
Computer(CPU* cpu,
VideoCard* videocard,
MainBoard* mainboard) {
elementList.push_back(cpu);
elementList.push_back(videocard);
elementList.push_back(mainboard);
};
void Accept(Visitor* visitor) {
for( std::vector<Element*>::iterator i = elementList.begin(); i != elementList.end(); i++ )
{
(*i)->accept(visitor);
}
};
private:
std::vector<Element*> elementList;
};
#endif
// main.cpp
#include "visitor.h"
int main() {
CPU* cpu = new CPU("Intel CPU");
VideoCard* videocard = new VideoCard("XXX video card");
MainBoard* mainboard = new MainBoard("HUAWEI mainboard");
Computer* myComputer = new Computer(cpu, videocard, mainboard);
CircuitDetector* Dan = new CircuitDetector("CircuitDetector Dan");
FunctionDetector* Tom = new FunctionDetector("FunctionDetector Tom");
std::cout << "\nStep 1: Dan is checking computer's circuits." << std::endl;
myComputer->Accept(Dan);
std::cout << "\nStep 2: Tom is checking computer's functions." << std::endl;
myComputer->Accept(Tom);
system("Pause");
return 0;
}
運行截圖:
參考資料:
《設計模式:可復用面向對象軟件的基礎》


![clipboard[1] clipboard[1]](/image/aHR0cHM6Ly9pbWFnZXMwLmNuYmxvZ3MuY29tL2Jsb2cvNTI2MzAzLzIwMTQwNS8yMDE3NDkzMTkzNDIxMjcucG5n.png)
![clipboard[2] clipboard[2]](/image/aHR0cHM6Ly9pbWFnZXMwLmNuYmxvZ3MuY29tL2Jsb2cvNTI2MzAzLzIwMTQwNS8yMDE3NDkzMjY2ODY5ODUucG5n.png)