在軟體開發中,當我們需要對一個複雜的物件結構執行各種操作,且這些操作會頻繁變動或新增時,若直接在物件類別中添加方法,會違反「開閉原則」並導致類別膨脹。訪問者模式提供了一種優雅的解決方案:將操作從物件結構中分離出來,封裝在獨立的訪問者類別中,使我們能在不修改現有類別的前提下定義新的操作。
訪問者模式屬於行為型模式,其核心在於「雙重分派」機制。當我們擁有一個相對穩定的物件結構(如文件樹、編譯器的抽象語法樹、GUI 元件樹),但需要對其執行多種且經常變化的操作(如匯出、列印、型別檢查、優化)時,此模式最為適合。它讓演算法與資料結構分離,新增操作只需新增訪問者類別,無需動到元素類別。
本範例展示一個訊息處理系統,包含 SQL 和 XML 兩種儲存方式。結構包含以下關鍵角色:
Accept(Visitor) 方法,所有具體元素需實作。SqlMessage、XmlMessage,實作 Accept 並呼叫訪問者的 Visit(this)。Visit 方法。InsertVisitor、GetVisitor,封裝具體操作邏輯。Messages 集合,負責遍歷元素並接受訪問者。
MessageModel 為資料承載類別,包含訊息內容與發布時間。AbstractElement 定義 Accept(AbstractVisitor) 抽象方法,強制子類別實作雙重分派入口。
兩者繼承自 Message(繼承 AbstractElement),各自實作 Get() 與 Insert() 具體邏輯。關鍵在於 Accept 方法:
public override string Accept(AbstractVisitor abstractVisitor)
{
return abstractVisitor.Visit(this);
}
這行程式碼實現了「雙重分派」:第一次分派由客戶端呼叫 element.Accept(visitor),第二次分派在元素內部呼叫 visitor.Visit(this),讓訪問者得知具體元素型別。
AbstractVisitor 宣告 Visit(AbstractElement)。具體訪問者 InsertVisitor 與 GetVisitor 分別封裝插入與查詢邏輯,透過型別轉換取得具體元素並呼叫對應方法:
public override string Visit(AbstractElement abstractElement)
{
Message m = abstractElement as Message;
return m.Insert().ToString() + "<br />";
}
Messages 類別維護元素集合,提供 Attach/Detach 管理元素,Accept 方法遍歷集合並彙總各元素接受訪問者的結果。客戶端程式碼展示如何組合使用:
Messages m = new Messages();
m.Attach(new SqlMessage(new MessageModel("插入", DateTime.Now)));
m.Attach(new XmlMessage(new MessageModel("插入", DateTime.Now)));
Response.Write(m.Accept(new InsertVisitor()));
Response.Write(m.Accept(new GetVisitor()));
程式輸出顯示兩種儲存方式的插入與查詢操作均成功執行:
True
True
SQL方式獲取Message 2007-5-27 15:01:53
XML方式獲取Message 2007-5-27 15:01:53
這證明訪問者模式成功將操作邏輯從元素類別中剝離,新增操作(如 DeleteVisitor)只需新增訪問者類別,完全不需修改 SqlMessage、XmlMessage 或 Messages。
訪問者模式透過雙重分派機制,將資料結構與操作演算法解耦,是處理複雜物件結構多樣化操作的利器。本範例以訊息儲存系統為例,完整展示了從抽象定義到具體實作、客戶端使用的全流程。實務上常見於編譯器 AST 遍歷、文件匯出、UI 元件樹操作等領域。掌握此模式,能讓你在面對「演算法變化多於資料結構」的系統時,擁有更靈活、可維護的架構設計能力。