Informazioni sul pattern del visitatore

15

Ho una gerarchia di classi che rappresenta i controlli della GUI. Qualcosa del genere:

Control->ContainerControl->Form

Devo implementare una serie di algoritmi che funzionano con oggetti che fanno cose varie e penso che il pattern Visitor sia la soluzione più pulita. Prendiamo ad esempio un algoritmo che crea una rappresentazione Xml di una gerarchia di oggetti. Usando l'approccio 'classico' lo farei:

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 di visitatore. Questa è l'implementazione di base:

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

Poiché anche le classi astratte aiutano l'implementazione, non sono sicuro di come farlo correttamente in ToXmlVisitor?

Il motivo per cui considero il pattern Visitor è che alcuni algoritmi necessiteranno di riferimenti non disponibili nel progetto in cui sono implementate le classi e ci sono un certo numero di algoritmi diversi, quindi sto evitando grandi classi.

    
posta Nezreli 23.10.2013 - 14:38
fonte

1 risposta

17

Il pattern visitor è un meccanismo per simulare il doppio binding nei linguaggi di programmazione che supporta 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 che stai utilizzando, gli oggetti possono essere convertiti in stringhe usando la funzione ToString() . Ciò che questa funzione, ovvero il codice che viene eseguito, dipende dal tipo di oggetto a cui lo stai applicando (è un metodo virtuale). Il codice che viene eseguito dipende da una cosa, l'unico tipo di oggetto, quindi il meccanismo utilizzato è chiamato binding singolo.

Ma cosa succede se voglio avere più di un modo per convertire un oggetto in una stringa, per ogni diverso tipo di oggetto? Cosa succede se volevo avere due modi per convertire gli oggetti in stringhe, in modo che il codice da eseguire dipenda da due cose: non solo l'oggetto da convertire, ma anche il modo in cui vogliamo convertirlo?

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

Il pattern visitor risolve il problema, trasformando il dual binding in due singoli binding succesivi.

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

Ma ciò implica che l'oggetto su cui si desidera applicare l'algoritmo deve collaborare con questo: ha bisogno di supporto per il pattern del visitatore cotto in.

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

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

Per ulteriori informazioni su come applicare quella tecnica, che ti permetterà di risolvere il tuo problema (se lo capisco bene), dai un'occhiata a Visitatori di addio .

UPDATE:

Per applicare la tecnica al tuo problema specifico, devi prima definire il tuo 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 inserisci 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
}
    
risposta data 23.10.2013 - 17:00
fonte

Leggi altre domande sui tag