Comprensione del modello di visitatore


16

Ho una gerarchia di classi che rappresenta i controlli della GUI. Qualcosa come questo:

Control->ContainerControl->Form

Devo implementare una serie di algoritmi che funzionano con oggetti che fanno varie cose e sto pensando che il modello Visitatore sarebbe la soluzione più pulita. Prendiamo ad esempio un algoritmo che crea una rappresentazione XML di una gerarchia di oggetti. Usando l'approccio 'classico' farei questo:

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;
    }
}

Ma non sono sicuro di come farlo con il modello visitatore. Questa è l'implementazione di base:

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

Dal momento che anche le classi astratte aiutano con l'implementazione non sono sicuro di come farlo correttamente in ToXmlVisitor?

La ragione per cui sto prendendo in considerazione il modello di Visitatore è che alcuni algoritmi avranno bisogno di riferimenti non disponibili nel progetto in cui sono implementate le classi e ci sono un certo numero di algoritmi diversi, quindi sto evitando classi di grandi dimensioni.


qual è la tua domanda?
moscerino il

Fondamentalmente come riscrivere il metodo ToXml () usando un modello visitatore.
Nezreli,


Grazie per il link L'invio dinamico semplifica il modello di visitatore tradizionale ma non cambia molto.
Nezreli,

@Nezreli Sì. Funziona con classi che non supportano il modello Visitor, come i controlli Windows Form con cui hai a che fare.
Kris Vandermotten,

Risposte:


17

Il modello visitatore è un meccanismo per simulare il doppio binding nei linguaggi di programmazione che supportano solo il binding singolo. Sfortunatamente, questa affermazione potrebbe non chiarire molto le cose, quindi lasciatemi spiegare con un semplice esempio.

In .NET e C #, la piattaforma in uso, gli oggetti possono essere convertiti in stringhe utilizzando la ToString()funzione. La funzione di tale funzione, ovvero il codice in esecuzione, dipende dal tipo di oggetto a cui lo stai applicando (è un metodo virtuale). Quale codice viene eseguito dipende da una cosa, dall'unico tipo di oggetto, quindi il meccanismo utilizzato è chiamato single binding.

E se volessi avere più di un modo per convertire un oggetto in una stringa, per ogni diverso tipo di oggetto? E se volessi avere due modi per convertire gli oggetti in stringhe, in modo che il codice in esecuzione dipenda da due cose: non solo l'oggetto da convertire, ma anche il modo in cui vogliamo che venga convertito?

Ciò potrebbe essere risolto bene se avessimo un doppio attacco. Ma la maggior parte dei linguaggi OO, incluso C #, supporta solo l'associazione singola.

Il modello visitatore risolve il problema, trasformando il doppio legame in due singoli vincoli successivi.

Nel nostro esempio sopra, userebbe un metodo virtuale nell'oggetto per convertire, che chiama un secondo metodo virtuale nell'oggetto che implementa l'algoritmo di conversione.

Ciò implica che l'oggetto su cui si desidera applicare l'algoritmo deve collaborare con questo: deve avere il supporto per il modello di visitatore inserito.

Sembra che tu stia utilizzando le classi Windows Form di .NET, che non hanno il supporto per il modello visitatore. Più specificamente, avrebbero bisogno di avere un public virtual void Accept(IVisitor)metodo, che ovviamente non hanno.

Allora, qual è l'alternativa? Bene, .NET non supporta solo il binding singolo, ma supporta anche il binding dinamico, che è ancora più potente del doppio binding.

Per ulteriori informazioni su come applicare quella tecnica, che ti consentirà di risolvere il tuo problema (se ho capito bene), dai un'occhiata a Farewell Visitor .

AGGIORNARE:

Per applicare la tecnica al problema specifico, innanzitutto definire il metodo di estensione:

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;
}

Crea il dispatcher dinamico:

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);
}

Quindi compilare i metodi specifici:

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
}

il dispacciamento dinamico in .NET è davvero piuttosto potente .. tuttavia ho notato che potrebbe essere un po '... beh .. lento, ma lo fa in una singola riga di codice che prende le linee sevral in più classi e si interfaccia con un visitatore
Newtopian

Tuttavia, il dispacciamento dinamico non risolverà il mio problema perché il mio algoritmo ToXml mi richiede di "visitare" tutti i tipi nella catena ereditaria. Nel mio esempio deve visitare Control, ContainterControl e Form in quell'ordine per avere una conversione XML corretta.
Nezreli,

@Nezreli Può risolvere il tuo problema, ho aggiornato la mia risposta per mostrarti come.
Kris Vandermotten,

Ho preso l'autorizzazione per aggiungere un commento alla definizione della variabile dinamica. Mi ci sono volute due letture del codice prima di individuarlo, ed è la chiave dell'intera storia.
Cristi Diaconescu,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.