Question

Je veux savoir si le dessous est une utilisation acceptable du motif des visiteurs. Je me sens un peu mal à l'aise de revenir d'un accept () ou visite () appel - est-ce une utilisation appropriée de ce modèle et si non, pourquoi pas

Remarque: Toutes mes excuses pour le long exemple de code, semble nécessaire de faire passer ce que je fais en tant que visiteur semble toujours être un peu impliqué ...

interface IAnimalElement<T>
{
   T Accept(IAnimalVisitor<T> visitor);
}

interface IAnimalVisitor<T>
{
    T Visit(Lion lion);
    T Visit(Peacock peacock);
    T VisitZoo(List<Animal> animals);
}

abstract class Animal
{
    public int Age { get; protected set; }
}

class Lion : Animal, IAnimalElement<int>
{
    public Lion(int age)
    {
        Age = age;
    }

    public int Accept(IAnimalVisitor<int> visitor)
    {
        return visitor.Visit(this);
    }
}

class Peacock : Animal, IAnimalElement<int>
{
    public Peacock(int age)
    {
        Age = age;
    }

    public int Accept(IAnimalVisitor<int> visitor)
    {
        return visitor.Visit(this);
    }
}

class AnimalAgeVisitor : IAnimalVisitor<int>
{
    public int TotalAge { get; private set; }

    int IAnimalVisitor<int>.Visit(Lion lion)
    {
        TotalAge += lion.Age;
        return lion.Age;
    }

    int IAnimalVisitor<int>.Visit(Peacock peacock)
    {
        TotalAge += peacock.Age + 10;
        return peacock.Age + 10; // peacocks ages are always -10y, correct.
    }

    public int VisitZoo(List<Animal> animals)
    {
        // Calculate average animal age.

        int sum = 0;
        int count = 0;
        foreach (IAnimalElement<int> animal in animals)
        {
            sum += animal.Accept(this);
            ++count;
        }

        return count == 0 ? 0 : sum / count;
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<Animal> animals = new List<Animal>() { new Lion(10), 
          new Lion(15), new Peacock(3), new Lion(2), new Peacock(9) };

        AnimalAgeVisitor visitor = new AnimalAgeVisitor();

        Console.WriteLine("Average age = {0}, Total age = {1}", 
            visitor.VisitZoo(animals), visitor.TotalAge);
    }
}
Était-ce utile?

La solution

Eh bien, pour moi cela se sent comme la mise en œuvre est un peu sur la clôture.

Soit votre visite et accepter les méthodes de retour vide et suivre l'ensemble de l'état dans l'objet des visiteurs. Interrogez à la fin.

ou ...

Avoir Visite et accepter un état de retour en cours et accepter un état en cours entrant d'une manière fonctionnelle.

Si vous optez pour la deuxième option, je ne suis pas vraiment sûr qu'un objet visiteur ou modèle est nécessaire, vous pouvez utiliser un itérateur, la fonction et un état transitoire à la place.

Autres conseils

Réponse courte:. Je ne vois pas de problème d'exposition d'un IVisitor retour d'un paramètre générique
Voir règles FxCop .

Il permet ensuite d'utiliser différents IVisitor chaque retour d'une valeur différente.

Cependant, dans votre cas , Visiteur n'est pas utile, puisque chaque animal a Âge biens ainsi tout peut être fait avec Animal ou un nouveau IAnimal interface.

Alternative utilise au prix de perdre multiples expédition typage fort .

Utilisez un Visiteur modèle lorsque vous voulez remplacer (ou éviter d'écrire) commutateur comme celui-ci:

IAnimal animal = ...;
switch (animal.GetType().Name)
{
  case "Peacock":
    var peacock = animal as Peacock;
    // Do something using the specific methods/properties of Peacock
    break;
  case "Lion":
    var peacock = animal as Lion;
    // Do something using the specific methods/properties of Lion
    break;
   etc...
}

ou emboîtés if-then-else équivalent.

Son but est d'acheminer l'instance à la routine pertinente pour son type en utilisant le polymorphisme et éviter laid if-then-else / instruction switch et moulages manuelles . De plus, elle contribue à diminuer couplage entre le code indépendant.

Alternative à cette question est d'ajouter une méthode virtuelle dans l'arborescence de la classe à visiter. Cependant, il est parfois impossible ou souhaitable:

  • visitable code de classe non modifiable (non propriété par exemple)
  • visitable code de classe non liée au code visite (ajoutant en classe signifierait abaisser le cohésion de la classe).

Voilà pourquoi il est souvent utilisé pour traverser un arbre d'objets (noeuds html, jetons analyseurs lexicaux, etc ...). Visiteur modèle implique les interfaces suivantes:

  • IVisitor

    /// <summary>
    /// Interface to implement for classes visiting others. 
    /// See Visitor design pattern for more details.
    /// </summary>
    /// <typeparam name="TVisited">The type of the visited.</typeparam>
    /// <typeparam name="TResult">The type of the result.</typeparam>
    public interface IVisitor<TVisited, TResult> : IVisitor where TVisited : IVisitable
    {
        TResult Visit(TVisited visited);
    }
    
    /// <summary>
    /// Marking interface.
    /// </summary>
    public interface IVisitor{}
    
  • IVisitable

    /// <summary>
    /// Interface to implement for classes visitable by a visitor.
    /// See Visitor design pattern for more details.
    /// </summary>
    /// <typeparam name="TVisitor">The type of the visitor.</typeparam>
    /// <typeparam name="TResult">The type of the result.</typeparam>
    public interface IVisitable<TVisitor, TResult> : IVisitable where TVisitor : IVisitor
    {
        TResult Accept(TVisitor visitor);
    }
    
    /// <summary>
    /// Marking interface.
    /// </summary>
    public interface IVisitable {}
    

La mise en œuvre de Accepter dans chaque IVisitable devrait appeler Visite (this) .

Il est assez commun. Je ne sais pas si vous pouvez le faire en C #, mais en Java, il est normal de laisser l'accepter méthode générique, donc ce qui est retourné est décidé par le visiteur pas visitee:

interface IAnimalElement
{
   <T> T Accept(IAnimalVisitor<T> visitor);
}


interface IAnimalVisitor<T> 
{
   T Visit(Peacock animal);
  ...
}

Pour les procédures, un IAnimalVisitor<Void> le renvoi peut être utilisé null.

La méthode visitable accepter n'est pas censé retourner quoi que ce soit. L'accepter est seulement censé indiquer le visiteur à visiter pendant ou après la visite.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top