2010-09-10 4 views
5

Bevor Sie mir sagen, dass es bereits eine ähnliche Frage ist, ja, ich weiß, ich habe it gelesen. Aber die Frage konzentriert sich auf , wenn, interessiert mich warum.Ein guter Grund, das Visitor-Designmuster zu verwenden?

Ich bekomme, wie die Dinge funktionieren. Der klassische Tier, Hund, Katze Beispiel funktioniert immer wie ein Charme.

Die Sache ist diesem Code

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

scheint mir so unnatürlich. Warum?

ich meine, ja, auf diese Weise ich meinen Hund und Katze Modelle undifferenziertes habe (erstes Mal, dass ich dieses Wort in Englisch btw verwenden), weil die wirkliche implentation unter der Sound-Klasse versteckt ist, aber nicht, dass nur ein Weg, um deinen Code zu beschweren? Ist Polymorphismus nicht genug, um so etwas zu tun?

Für mich ist der Unterschied, dass mit Polymorphie Sie jede Klasse bearbeiten müssen (aber das Modell bleibt gleich, oder?), Während Sie nur eine Klasse mit dem Besucherentwurfsmuster bearbeiten müssen.

Antwort

8

Mit dem Besuchermuster können Sie etwas tun, was sich einfach nicht auf Polymorphie stützt: mit unerwarteten Anwendungsfällen arbeiten. Wenn Sie eine Bibliothek schreiben, ist dies ein wichtiger Punkt. Lassen Sie mich näher ausführen:

Betrachten Sie ein klassisches Beispiel für die Verwendung des Besuchermusters, nämlich die Operation auf den Knoten von einigen abstract syntax Baum. Um einige Details hinzuzufügen, sagen Sie, Sie haben gerade eine Parser-Bibliothek für SQL geschrieben, die Strings aufnimmt, sie analysiert und einen AST für den Inhalt zurückgibt, den sie in der Eingabe gefunden hat. Sofern Sie nicht alle potenziellen Anwendungsfälle vorhersehen können, die Ihr Kundencode für einen solchen AST haben könnte, müssen Sie eine "generische" Möglichkeit bereitstellen, den AST zu durchlaufen. Das Bereitstellen von DOM-artigen Zugriffsfunktionen (, getParentNode, getPreviousNode) ist ein Weg. Das Problem hierbei ist, dass dies die Clients Ihrer Bibliothek stark belastet, da sie die Aufgabe selbst erledigen müssen. Noch mehr, müssen sie im Detail kennen, die für jeden möglichen Knotentyp folgen Zeiger:

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

Das Besuchermuster bewegt sich diese Last von dem Client in die Bibliothek.

+0

Genau kann das Verhalten von einer dritten Partei implementiert werden. –

+0

Ok, aber wenn Sie Parser für SQL definieren, haben Sie nicht schon Ihre Grammatikdefinition? Wo sind die unerwarteten Anwendungsfälle? Eigentlich, da wir über das Parsing sprechen, sollte kein Parser-Generator ein passenderes Beispiel sein? Dort haben Sie eine willkürliche Grammatik, dann müssen Sie eine generische Tree-Walker-Klasse definieren. – dierre

+0

Es geht nicht um die Struktur des SQL. Es geht darum, wie der Client das Ergebnis/die Ausgabe Ihrer Bibliothek verwenden kann. Das Parser-Beispiel wird einfach verwendet, weil es etwas "klassisch" ist. Übrigens, wenn Sie eine vollständig generische API (beliebige "DOM" -ähnliche Knoten) ohne eine feste Struktur haben, könnten Sie besser mit dem generischen Accessor-Ansatz anstatt mit dem Besucher-Muster arbeiten. – Dirk

3

Nehmen wir an, Sie haben einige grundlegende Sachen definiert in einer Bibliothek, die Sie nicht besitzen, und Sie müssen es erweitern. Wie:

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

class Something1 : ISomething { 
    // ... 
} 

class Something2 : ISomething { 
    // ... 
} 

Polymorphismus können Sie neue Dinge definieren Sie können Operationen durchführen auf:

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

Und nun die Basis lib kann mit Ihrem MySomething arbeiten, als ob sie es definiert hatte. Was es nicht lässt Sie tun, ist neue Operationen hinzuzufügen. DoSomething ist das einzige, was wir mit einem ISomething tun können. Das Besuchermuster adressiert das.

Der Nachteil ist, dass die Verwendung des Besuchermusters kostet Sie die Fähigkeit, neue Typen zu definieren, wie wir gerade gezeigt haben.Die Tatsache, dass Sie in den meisten Sprachen Operationen oder Typen einfach, aber nicht beides hinzufügen können, wird expression problem genannt.

Das Besuchermuster ist ein cooles, aber ich habe es nie außerhalb der Implementierung von Compilern gefunden.

+0

Sie können keine neuen Typen hinzufügen, da Ihr Besucher immer noch eine Komponente der Bibliothek ist, richtig? – dierre

+1

Die Besucherklasse wird in der Basisbibliothek definiert und hat eine feste Menge von Methoden, eine für jeden Typ. Sie können in Ihrer Lib keine neuen Typen hinzufügen, da Sie den Besucher nicht ändern können und Sie den Besucher nicht in Ihre Lib verschieben können, da die Typen in der Basisbibliothek in ihren 'accept()' Methoden darauf verweisen müssen. – munificent

+0

haben es geschafft. Vielen Dank! – dierre

1

Ich verwendete das Besuchermuster, wenn ich einen Baum von Objekten hatte und den Inhalt auf verschiedene Arten drucken musste. Komma-sep, XML, was auch immer. Anstatt der Baumklasse eine neue Druckmethode für jedes Ausgabeformat hinzuzufügen, habe ich das Besuchermuster verwendet und die Klassen CommaSepVisitor, XMLVisitor und HTMLVisitor erstellt. Der Baumcode hat sich nie geändert, da ich mehr Visitor-Typen hinzugefügt habe, sodass ich nie Bugs eingeführt habe. Die Besucher selbst waren einfach zu schreiben.

2

Das Besuchermuster ist sehr nützlich.

Es gibt mindestens drei große Gründe für die Verwendung es:

  1. Proliferation von Code reduzieren, die nur etwas anders, wenn die Datenstrukturen ändern.

  2. Wenden Sie die gleiche Berechnung auf mehrere Datenstrukturen an, ohne den Code zu ändern, der die Berechnung implementiert.

  3. Fügen Sie Informationen zu Legacy-Bibliotheken hinzu, ohne den Legacy-Code zu ändern.

Bitte sehen Sie sich an article I've written about this an.

Prost

+0

Tnx, ich werde es lesen. – dierre

Verwandte Themen