2010-09-10 79 views
5

在你告訴我已經有類似的問題之前,我知道,我讀過it。 但是這個問題重在,我有興趣爲什麼使用Visitor設計模式的一個很好的理由是什麼?

我知道事情是如何運作的。經典的動物,狗,貓的例子總是像一個魅力。

事情是這樣的代碼

int main() 
{ 
    Cat c; 
    Sound theSound; 
    c.letsDo(&theSound); 
} 

顯得那麼不自然的我。爲什麼?

我的意思是,是啊,這樣我有我的狗和貓的模型未分化(我第一次用英語BTW這個詞),因爲真正的implentation是聲音類下隱藏而不是隻是一個如何權衡你的代碼?多態不足以完成這樣的事情嗎?

對我來說,不同的是,多態性你必須編輯每個類(但模型保持不變,對吧?),而你只需要編輯一個類與訪客設計模式。

回答

8

訪問者模式允許您執行某些操作,它僅僅依賴於多態性不會:使用意料之外的用例。如果你正在寫圖書館,這是一個重要的觀點。讓我詳細說明一下:

考慮一個使用訪問者模式的經典例子,即對某些abstract syntax樹的節點進行操作。要添加一些細節,比如說,你剛剛寫了一個SQL解析器庫,它需要字符串,解析它們,併爲它在輸入中找到的東西返回一個AST。除非您可以預測您的客戶端代碼可能具有這種AST的所有潛在用例,否則您必須提供一種「通用」方式來執行AST。提供類似DOM的訪問器功能(getNodeType,getParentNode,getPreviousNode)是一種方法。這裏的問題在於,這會給你的圖書館的客戶帶來沉重的負擔,因爲他們需要自己去做調度。更有甚者,他們需要非常詳盡,其中指針遵循對每一可能的節點類型知道:

void 
walk_tree(AstNode* node) 
{ 
    switch(node->getNodeType()) { 
    case SELECT_NODE: 
     for(AstNode* child = node->getFirstChild(); child; child = child->getNextNode()) { 
      walk_tree(child); 
     } 
     break; 
    ... 
    } 
} 

Visitor模式從客戶端轉移這種負擔到庫中。

+0

準確地說,行爲可以由第三方實施。 – 2010-09-10 15:15:14

+0

好的,但是如果你爲SQL定義解析器,你是否已經有了語法定義?意外用例在哪裏?其實,因爲我們正在談論解析,不應該是一個更合適的例子解析器生成器?在那裏你有一個任意的語法,所以你必須定義一個通用樹walker類。 – dierre 2010-09-10 16:41:21

+0

這不是關於SQL的結構。它關於客戶如何使用庫的結果/輸出。解析器的例子很簡單,因爲它有點「經典」。順便說一句,如果你有一個沒有固定結構的完全通用的API(任意「類似DOM」的節點),你最好用通用訪問方法而不是訪問者模式。 – Dirk 2010-09-11 18:22:44

3

比方說,你已經有一些基本的東西定義在你不擁有的庫中,你需要擴展它。像:

// In base lib: 
interface ISomething { 
    void DoSomething(); 
} 

class Something1 : ISomething { 
    // ... 
} 

class Something2 : ISomething { 
    // ... 
} 

多態性讓你定義一個新的東西,你可以執行操作:

// In your lib: 
class MySomething : ISomething { 
} 

而且現在的基部11b可以與您的MySomething工作,就好像它定義它。它不是讓你做的是增加新的操作。我們可以用ISomething做的唯一事情就是DoSomething。訪問者模式解決了這個問題。

缺點是使用訪問者模式需要花費您可以像我們剛剛展示的那樣定義新類型。事實上,大多數語言可以讓您輕鬆地添加操作或類型,但不能同時添加這兩種語言,稱爲expression problem

訪客模式是一個很酷的模式,但我從來沒有在實現編譯器之外找到它的需要。

+0

您無法添加新類型,因爲您的訪問者仍然是圖書館的一個組成部分,對吧? – dierre 2010-09-13 08:53:48

+1

訪客類將在基本庫中定義,並將有一組固定的方法,每種類型一個。你不能在你的lib中添加新的類型,因爲你不能改變訪問者,並且你不能將訪問者移動到你的lib中,因爲base lib中的類型需要在它們的'accept()'方法中引用它。 – munificent 2010-09-13 14:24:09

+0

明白了。謝謝! – dierre 2010-09-13 16:23:04

1

當我有一個對象樹並需要以多種方式打印內容時,我使用了訪問者模式。逗號,XML,不管。我使用訪問者模式並創建了CommaSepVisitor,XMLVisitor和HTMLVisitor類,而不是爲每個輸出格式添加新的打印方法。樹代碼從未改變,因爲我添加了更多的訪問者類型,所以我從來沒有引入錯誤。參觀者本身很容易寫作。

2

訪客模式非常有用。

有使用它至少有三個重要原因:

  1. 減少代碼增殖當數據結構變化的情況下僅略有不同。

  2. 將相同的計算應用於多個數據結構,而不改變實現計算的代碼。

  3. 將信息添加到舊版庫而不更改舊版代碼。

請看看an article I've written about this

乾杯

+0

tnx,我要讀它。 – dierre 2011-01-30 02:26:01

相關問題