Visitor訪問器模式是一種“行爲變化”模式
- 在組件的構建過程中,組件行爲的變化經常導致組件本身劇烈的變化。“行爲變化”模式將組件的行爲和組件本身進行解耦,從而支持組件行爲的變化,實現兩者之間的松耦合
動機
- 在軟件構建過程中,由於需求的改變,某些類層次結構中常常需要增加新的行爲(方法),如果直接在基類中做這樣的更改,將會給子類帶來很繁重的變更負擔,甚至破壞原有設計
- 如何在不更改類層次結構的前提下,在運行時根據需要透明地位類層次結構上的各個類動態添加新的操作,從而避免上述問題?
定義
- 表示一個作用於某對象結構中的各元素的操作**。使得可以在不改變(穩定)各元素的類的前提下定義(擴展)作用於這些元素的新操作(變化)**
結構
代碼對比
visitor1.cpp
#include <iostream>
using namespace std;
class Visitor;
class Element
{
public:
virtual void Func1() = 0;
virtual void Func2(int data)=0;
virtual void Func3(int data)=0;
//...
virtual ~Element(){}
};
class ElementA : public Element
{
public:
void Func1() override{
//...
}
void Func2(int data) override{
//...
}
};
class ElementB : public Element
{
public:
void Func1() override{
//***
}
void Func2(int data) override {
//***
}
};
visitor2.cpp
#include <iostream>
using namespace std;
class Visitor;
class Element
{
public:
virtual void accept(Visitor& visitor) = 0; //第一次多態辨析
virtual ~Element(){}
};
class ElementA : public Element
{
public:
void accept(Visitor &visitor) override {
visitor.visitElementA(*this);
}
};
class ElementB : public Element
{
public:
void accept(Visitor &visitor) override {
visitor.visitElementB(*this); //第二次多態辨析
}
};
class Visitor{
public:
virtual void visitElementA(ElementA& element) = 0;
virtual void visitElementB(ElementB& element) = 0;
virtual ~Visitor(){}
};
//==================================
//擴展1
class Visitor1 : public Visitor{
public:
void visitElementA(ElementA& element) override{
cout << "Visitor1 is processing ElementA" << endl;
}
void visitElementB(ElementB& element) override{
cout << "Visitor1 is processing ElementB" << endl;
}
};
//擴展2
class Visitor2 : public Visitor{
public:
void visitElementA(ElementA& element) override{
cout << "Visitor2 is processing ElementA" << endl;
}
void visitElementB(ElementB& element) override{
cout << "Visitor2 is processing ElementB" << endl;
}
};
int main()
{
Visitor2 visitor;
ElementB elementB;
elementB.accept(visitor);// double dispatch
ElementA elementA;
elementA.accept(visitor);
return 0;
}
對比
- visitor1.cpp中由於需求的變化,如果要在部署、分發後給基類接口添加功能的話,一般的做法是在接口類
Element
中直接添加方法Func2
,然後在派生類中override這個方法。這違背了開閉原則,給子類帶來了繁重的變更負擔,甚至破壞了原有設計
- visitor2.cpp先在基類中預置了一個
accept(visitor&)
的方法接口,這個接口包含將來可能會增加的一些操作。在要添加具體的操作時,定義繼承visitor
基類的派生子類,再override相關的訪問和操作函數visitElement
。在調用時直接使用element.accept(visitor)
,這樣具體運行時element
是多態的,accept(visitor)
也是多態的,即double dispatch(二次多態辨析)
要點總結
- Visitor模式通過所謂雙重分發(double dispatch)來實現在不更改(不添加新的操作-編譯時)Element類層次結構的前提下,在運行時透明地位類層次結構上的各個類動態添加新的操作(支持變化)
- 所謂雙重分發即Visitor模式中間包括了兩個多態分發(注意其中的多態機制):第一個爲
accept
方法的多態辨析;第二個爲visitElementX
方法的多態辨析
- Visitor模式的最大缺點在於擴展類層次結構(增添新的Element子類),會導致Visitor類的改變。因此Visitor模式適用於“Element類層次結構穩定,而其中的操作卻經常面臨頻繁改動”的情形