物件導向系統的開發和設計過程,經常會遇到一種情況就是需求變更(requirement changing),經常做好的乙個設計、實現了乙個系統原型,客戶又會有了新的需求。因此不得不去修改已有的設計,最常見就是解決方案就是給已經設計、實現好的類新增新的方法去實現客戶新的需求,這樣就陷入了設計變更的夢魘:不停地打補丁,其帶來的後果就是設計根本就不可能封閉、編譯永遠都是整個系統**。
visitor模式則提供了一種解決方案:將更新(變更)封裝到乙個類中(訪問操作),並由待更改類提供乙個接收介面,則可達到效果。
visitor模式經常用於將更新的設計封裝在乙個類中,並且由待更改的類提供乙個接受介面,其關鍵技術在於雙分派技術,如圖所示,element類提供介面,通過accept實現具體使用哪乙個具體的visit操作;
當然如果有很多的修改,便可以提供更多的element的visitor,但是會破壞系統的封裝,並且難於擴充套件。
!!!
也就是說,如果一組類想增加乙個操作,那麼,先在基類中定義乙個virtual accept(visite&)介面,然後,子類過載該該accept.指定具體的visitor的指定操作.為該組類定義乙個visitor基類,針對具體某個類的改變要麼各個派生乙個visit要麼,只派生乙個visit,往其中加入對應於不同的類操作的函式. 也就是說.你也可以,把對多個類的更改都集中於乙個visitor中.在visitor中多提供幾個介面eg visiteelemeta() visitelemetb().visiteelementc()....
再提醒一次,對乙個類執行多個操作時,必須從visitor中派生出多個visit!!!.對多個類執行乙個操作.可用乙個visitor.
visitor模式表示乙個作用於某物件結構中的各元素的操作.它使我們可以在不改變各元素的類
的前提下定義作用於這些元素的新操作.
visitor模式把對結點的訪問封裝成乙個抽象基類,通過派生出不同的類生成新的訪問方式.實現時,在visitor抽象基類中宣告了對所有不同結點進行訪問的介面函式。visitor模式的有乙個缺陷--新加入乙個結點時都要新增visitor中的對其進行訪問介面函式,這樣使得所有的visitor及其派生類都要重新編譯了,也就是說visitor模式乙個缺點就是新增新的結點十分困難.另外,還需要指出的是visitor模式採用了所謂的"雙重分派"的技術。
visitor模式在不破壞類的前提下,為類提供增加新的新操作。visitor模式的關鍵是雙分派(double-dispatch)的技術。c++語言支援的是單分派。
#ifndef visitor_h
#define visitor_h
class visitor;
class element
virtual void accept(visitor &rvisitor) = 0;
protected:
element(){}
};class concreateelementa : public element
virtual void accept(visitor &rvisitor);
};class concreateelementb : public element
virtual void accept(visitor &rvisitor);
};class visitor
virtual void visitconcreateelementa(concreateelementa *pconcreateelement
a) = 0;
virtual void visitconcreateelementb(concreateelementb *pconcreateelement
b) = 0;
protected:
visitor(){}
};class concreatevisitora : public visitor
virtual void visitconcreateelementa(concreateelementa *pconcreateelement
a);virtual void visitconcreateelementb(concreateelementb *pconcreateelement
b);};
class concreatevisitorb : public visitor
virtual void visitconcreateelementa(concreateelementa *pconcreateelement
a);virtual void visitconcreateelementb(concreateelementb *pconcreateelement
b);};
#endif
#include "visitor.h"
#include
void concreateelementa::accept(visitor &rvisitor)
void concreateelementb::accept(visitor &rvisitor)
void concreatevisitora::visitconcreateelementa(concreateelementa *pconcreat
eelementa)
void concreatevisitora::visitconcreateelementb(concreateelementb *pconcreat
eelementa)
void concreatevisitorb::visitconcreateelementa(concreateelementa *pconcreat
eelementa)
void concreatevisitorb::visitconcreateelementb(concreateelementb *pconcreat
eelementa)
#include "visitor.h"
int main()
在visitor模式中accept()操作是乙個雙分派的操作。具體呼叫哪乙個具體的accept()操作,有兩個決定因素:1)element的型別。因為accept()是多型的操作,需要具體的element型別的子類才可以決定到底呼叫哪乙個accept()實現;2)visitor的型別。accept()操作有乙個引數(visitor* vis),要決定了實際傳進來的visitor的實際類別才可以決定具體是呼叫哪個visitconcrete()實現。
visitor模式的實現過程中有以下的地方要注意:
visitor類中的visit()操作的實現。這裡我們可以向element類僅僅提供乙個介面visit(),而在accept()實現中具體呼叫哪乙個visit()操作則通過函式過載(overload)的方式實現:我們提供visit()的兩個過載版本:a)visit(concreteelementa* elma),b)visit(concreteelementb* elmb)。
c++中還可以通過rtti(執行時型別識別:runtime type identification)來實現,即我們只提供乙個visit()函式體,傳入的引數為element*型別引數 ,然後用rtti決定具體是哪一類的concreteelement引數,再決定具體要對哪個具體類施加什麼樣的具體操作。rtti給介面帶來了簡單一致性,但是付出的代價是時間(rtti的實現)和**的hard編碼(要進行強制轉換)。
visitor模式可以使得element在不修改自己的同時增加新的操作,但是這也帶來了至少以下的兩個顯著問題:
1) 破壞了封裝性。visitor模式要求visitor可以從外部修改element物件的狀態,這一般通過兩個方式來實現:a)element提供足夠的public介面,使得visitor可以通過呼叫這些介面達到修改element狀態的目的;b)element暴露更多的細節給visitor,或者讓element提供public的實現給visitor(當然也給了系統中其他的物件),或者將visitor宣告為element的friend類,僅將細節暴露給visitor。但是無論那種情況,特別是後者都將是破壞了封裝性原則。
2) concreteelement的擴充套件很困難:每增加乙個element的子類,就要修改visitor的介面,使得可以提供給這個新增加的子類的訪問機制。從上面我們可以看到,或者增加乙個用於處理新增類的visit()介面,或者過載乙個處理新增類的visit()操作,或者要修改rtti方式實現的visit()實現。無論那種方式都給擴充套件新的element子類帶來了困難。
點評:文中說 accept()是乙個雙分派的操作.但想一下,c++是不支援雙分派的.這不是矛盾嗎?
事實上,說c++不支援雙分派,僅當出現兩個同名函式.僅引數不一樣,(差別在於乙個是基類指標,乙個派生類指標)時,才表現出不支援double dispatch.正常情況下,c++會表現出你所期望的那樣.本例中,element::accept()僅有乙個同名函式,所以,不存在double diapatch()之說.
設計模式之Visitor模式(筆記)
訪問者模式 表示乙個作用於某個物件結構中的各元素操作。它使你能夠不改變各元素的類的前提下定義作用於這些元素的新操作。首先定義乙個visitor抽象類,為每乙個詳細類宣告乙個visit操作 public abstract class visitor 接著定義繼承visitor的詳細類 public c...
設計模式 訪問者模式 Visitor
visitor模式表示乙個作用於某物件結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用於這些元素的新操作。類圖 乙個物件結構包含很多類物件,它們有不同的介面,而你想對這些物件實施一些依賴於其具體類的操作。需要對乙個物件結構中的物件進行很多不同的並且不相關的操作,而你想避免讓這些操作...
設計模式 訪問者 Visitor 模式
from 參考 訪問者模式的目的是封裝一些施加於某種資料結構元素之上的操作。一旦這些操作需要修改的話,接受這個操作的資料結構則可以保持不變。第一次這句話時,著實費解。關於對訪問者模式的理解,可以先見下面這個例子,而後再看最後的綜述。比如 你下午請客喝東西,那麼就的考慮好你請的這些人中總會有口味不同,...