Besuchermuster verstehen

16

Ich habe eine Hierarchie von Klassen, die GUI-Steuerelemente darstellt. Etwas wie das:

Control->ContainerControl->Form

Ich muss eine Reihe von Algorithmen implementieren, die mit Objekten arbeiten, die verschiedene Aufgaben ausführen, und ich denke, dass das Besuchermuster die sauberste Lösung wäre. Nehmen wir zum Beispiel einen Algorithmus, der eine XML-Darstellung einer Hierarchie von Objekten erzeugt. Unter Verwendung des klassischen Ansatzes würde ich dies tun:

public abstract class Control
{
    public virtual XmlElement ToXML(XmlDocument document)
    {
        XmlElement xml = document.CreateElement(this.GetType().Name);
        // Create element, fill it with attributes declared with control
        return xml;
    }
}

public abstract class ContainerControl : Control
{
    public override XmlElement ToXML(XmlDocument document)
    {
        XmlElement xml = base.ToXML(document);
        // Use forech to fill XmlElement with child XmlElements
        return xml;
    }
}

public class Form : ContainerControl
{
    public override XmlElement ToXML(XmlDocument document)
    {
        XmlElement xml = base.ToXML(document);
        // Fill remaining elements declared in Form class
        return xml;
    }
}

Aber ich bin mir nicht sicher, wie ich das mit dem Besuchermuster machen soll. Dies ist die grundlegende Implementierung:

public class ToXmlVisitor : IVisitor
{
    public void Visit(Form form)
    {
    }
}

Da auch die abstrakten Klassen bei der Implementierung helfen, bin ich mir nicht sicher, wie ich das in ToXmlVisitor richtig machen soll?

Der Grund, warum ich das Besuchermuster in Betracht ziehe, ist, dass einige Algorithmen Referenzen benötigen, die in Projekten, in denen die Klassen implementiert sind, nicht verfügbar sind, und dass es eine Reihe verschiedener Algorithmen gibt, sodass ich große Klassen vermeide.

Nezreli
quelle
Was ist deine Frage?
gnat
Grundsätzlich wird beschrieben, wie die ToXml () - Methode mithilfe eines Besuchermusters umgeschrieben wird.
Nezreli
Werfen
Kris Vandermotten
Danke für den Link. Der dynamische Versand vereinfacht das traditionelle Besucherverhalten, ändert aber nicht viel.
Nezreli
@Nezreli Ja, das tut es. Es funktioniert mit Klassen, die das Besuchermuster nicht unterstützen, z. B. mit den Windows Forms-Steuerelementen, mit denen Sie arbeiten.
Kris Vandermotten

Antworten:

17

Das Besuchermuster ist ein Mechanismus zur Simulation der Doppelbindung in Programmiersprachen, die nur die Einzelbindung unterstützen. Leider könnte diese Aussage die Dinge nicht sehr klar machen. Lassen Sie mich dies anhand eines einfachen Beispiels erläutern.

In .NET und C #, der von Ihnen verwendeten Plattform, können Objekte mithilfe der ToString()Funktion in Zeichenfolgen konvertiert werden . Was diese Funktion, dh der ausgeführte Code, tut, hängt von der Art des Objekts ab, auf das Sie ihn anwenden (es ist eine virtuelle Methode). Welcher Code ausgeführt wird, hängt von einer Sache ab, dem einen Typ des Objekts. Daher wird der verwendete Mechanismus als Einzelbindung bezeichnet.

Aber was ist, wenn ich mehr als eine Möglichkeit haben möchte, ein Objekt für jede Art von Objekt in einen String umzuwandeln? Was wäre, wenn ich zwei Möglichkeiten hätte, Objekte in Zeichenfolgen zu konvertieren, damit der auszuführende Code von zwei Dingen abhängt: nicht nur vom zu konvertierenden Objekt, sondern auch von der Art und Weise, wie wir es konvertieren möchten?

Das könnte gut gelöst werden, wenn wir eine doppelte Bindung hätten. Die meisten OO-Sprachen, einschließlich C #, unterstützen jedoch nur die Einzelbindung.

Das Besuchermuster löst das Problem, indem die Doppelbindung in zwei aufeinanderfolgende Einzelbindungen umgewandelt wird.

In unserem obigen Beispiel würde für die Konvertierung eine virtuelle Methode im Objekt verwendet, die eine zweite virtuelle Methode im Objekt aufruft, die den Konvertierungsalgorithmus implementiert.

Dies impliziert jedoch, dass das Objekt, auf das Sie den Algorithmus anwenden möchten, damit zusammenarbeiten muss: Es muss Unterstützung für das eingebrachte Besuchermuster haben.

Sie verwenden anscheinend .NETs Windows Forms-Klassen, die das Besuchermuster nicht unterstützen. Insbesondere müssten sie eine public virtual void Accept(IVisitor)Methode haben, die sie offensichtlich nicht haben.

Also, was ist die Alternative? Nun, .NET unterstützt nicht nur die Einzelbindung, sondern auch die dynamische Bindung, die noch leistungsfähiger ist als die Doppelbindung.

Weitere Informationen zur Anwendung dieser Technik, mit der Sie Ihr Problem lösen können (sofern ich sie gut verstehe), finden Sie unter Farewell Visitor .

AKTUALISIEREN:

Um die Technik auf Ihr spezielles Problem anzuwenden, definieren Sie zunächst Ihre Erweiterungsmethode:

public static XmlDocument ToXml(this Control control)
{
    XmlDocument xml = new XmlDocument();
    XmlElement root = xml.CreateElement(control.GetType().Name);
    xml.AppendChild(root);

    Visit(control, xml, root);

    return xml;
}

Erstellen Sie den dynamischen Dispatcher:

private static void Visit(Control control, XmlDocument xml, XmlElement root)
{
    dynamic dynamicControl = control; //notice the 'dynamic' type.
                                      //this is the key to dynamic dispatch

    VisitCore(dynamicControl, xml, root);
}

Füllen Sie dann die spezifischen Methoden aus:

private static void VisitCore(Control control, XmlDocument xml, XmlElement root)
{
    // TODO: specific Control handling
}

private static void VisitCore(ContainerControl control, XmlDocument xml, XmlElement root)
{
    // call the "base" method
    VisitCore(control as Control, xml, root);

    // TODO: specific ContainerControl handling
    // for example:
    foreach (Control child in control.Controls)
    {
        XmlElement element = xml.CreateElement(child.GetType().Name);
        root.AppendChild(element);

        // call the dynamic dispatcher method
        Visit(child, xml, element);
    }
}

private static void VisitCore(Form control, XmlDocument xml, XmlElement root)
{
    // call the "base" method
    VisitCore(control as ContainerControl, xml, root);

    // TODO: specific Form handling
}
Kris Vandermotten
quelle
dynamischer Versand in .NET ist in der Tat ziemlich leistungsfähig. Ich habe jedoch festgestellt, dass es ein bisschen ... naja ... langsam sein kann, aber es funktioniert in einer einzigen Codezeile, was mehrere Zeilen in mehreren Klassen und Schnittstellen mit einem Besucher benötigt
Newtopian
Der dynamische Versand kann mein Problem jedoch nicht lösen, da ich nach meinem ToXml-Algorithmus alle Typen in der Vererbungskette "besuchen" muss. In meinem Beispiel müssen Control, ContainterControl und Form in dieser Reihenfolge aufgerufen werden, um eine erfolgreiche XML-Konvertierung durchzuführen.
Nezreli
@Nezreli Es kann Ihr Problem lösen, ich habe meine Antwort aktualisiert, um Ihnen zu zeigen, wie.
Kris Vandermotten
Ich habe die Erlaubnis erhalten, der dynamischen Variablendefinition einen Kommentar hinzuzufügen. Ich habe den Code zwei Mal gelesen, bevor ich ihn entdeckt habe, und er ist der Schlüssel zur ganzen Geschichte.
Cristi Diaconescu