Comprendre le modèle de visiteur

16

J'ai une hiérarchie de classes qui représente les contrôles GUI. Quelque chose comme ça:

Control->ContainerControl->Form

Je dois implémenter une série d'algorithmes qui fonctionnent avec des objets faisant diverses choses et je pense que le modèle de visiteur serait la solution la plus propre. Prenons par exemple un algorithme qui crée une représentation XML d'une hiérarchie d'objets. En utilisant une approche «classique», je ferais ceci:

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

Mais je ne sais pas comment faire cela avec le modèle de visiteur. Voici l'implémentation de base:

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

Étant donné que même les classes abstraites aident à l'implémentation, je ne sais pas comment le faire correctement dans ToXmlVisitor?

La raison pour laquelle je considère le modèle de visiteur est que certains algorithmes auront besoin de références non disponibles dans le projet où les classes sont implémentées et qu'il existe un certain nombre d'algorithmes différents, donc j'évite les grandes classes.

Nezreli
la source
quelle est ta question?
moucher
Fondamentalement, comment réécrire la méthode ToXml () en utilisant un modèle de visiteur.
Nezreli
Merci pour le lien. La répartition dynamique simplifie le modèle de visiteur traditionnel, mais elle ne change pas beaucoup.
Nezreli
@Nezreli Oui, c'est le cas. Il fonctionne avec les classes qui ne prennent pas en charge le modèle Visitor, telles que les contrôles Windows Forms avec lesquels vous traitez.
Kris Vandermotten

Réponses:

17

Le modèle de visiteur est un mécanisme permettant de simuler une double liaison dans des langages de programmation qui ne prennent en charge qu'une seule liaison. Malheureusement, cette déclaration peut ne pas clarifier beaucoup les choses, alors laissez-moi vous expliquer avec un exemple simple.

Dans .NET et C #, la plateforme que vous utilisez, les objets peuvent être convertis en chaînes à l'aide de la ToString()fonction. Ce que fait cette fonction, c'est-à-dire le code en cours d'exécution, dépend du type d'objet auquel vous l'appliquez (c'est une méthode virtuelle). Le code exécuté dépend d'une chose, du type d'objet, c'est pourquoi le mécanisme utilisé est appelé liaison simple.

Mais que se passe-t-il si je veux avoir plus d'une façon de convertir un objet en chaîne, pour chaque type d'objet différent? Et si je voulais avoir deux façons de convertir des objets en chaînes, de sorte que le code en cours d'exécution dépend de deux choses: non seulement l'objet à convertir, mais aussi la manière dont nous voulons qu'il soit converti?

Cela pourrait être bien résolu si nous avions une double liaison. Mais la plupart des langages OO, y compris C #, ne prennent en charge que la liaison unique.

Le modèle de visiteur résout le problème en transformant la double liaison en deux liaisons simples successives.

Dans notre exemple ci-dessus, il utiliserait une méthode virtuelle dans l'objet à convertir, qui appelle une deuxième méthode virtuelle dans l'objet implémentant l'algorithme de conversion.

Mais cela implique que l'objet sur lequel vous souhaitez appliquer l'algorithme doit collaborer avec cela: il doit prendre en charge le modèle de visiteur intégré.

Vous semblez utiliser les classes Windows Forms de .NET, qui ne prennent pas en charge le modèle de visiteur. Plus précisément, ils auraient besoin d'une public virtual void Accept(IVisitor)méthode, ce qu'ils n'ont évidemment pas.

Alors, quelle est l'alternative? Eh bien, .NET ne prend pas seulement en charge la liaison simple, il prend également en charge la liaison dynamique, ce qui est encore plus puissant que la double liaison.

Pour plus d'informations sur la façon d'appliquer cette technique, qui vous permettra de résoudre votre problème (si je le comprends bien), jetez un œil à Farewell Visitor .

MISE À JOUR:

Pour appliquer la technique à votre problème spécifique, définissez d'abord votre méthode d'extension:

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

Créez le répartiteur dynamique:

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

Remplissez ensuite les méthodes spécifiques:

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
la source
la répartition dynamique dans .NET est en effet assez puissante .. mais j'ai remarqué que cela pourrait être un peu ... eh bien .. lent, mais il le fait dans une seule ligne de code ce qui prend plusieurs lignes dans plusieurs classes et interfaces avec un visiteur
Newtopian
Pourtant, la répartition dynamique ne résoudra pas mon problème car mon algorithme ToXml m'oblige à «visiter» tous les types de la chaîne d'héritage. Dans mon exemple, il doit visiter Control, ContainterControl et Form dans cet ordre pour réussir la conversion XML.
Nezreli
@Nezreli Cela peut résoudre votre problème, j'ai mis à jour ma réponse pour vous montrer comment.
Kris Vandermotten
J'ai pris la permission d'ajouter un commentaire à la définition de variable dynamique. Il m'a fallu deux lectures du code avant de le repérer, et c'est la clé de toute l'histoire.
Cristi Diaconescu