2010-10-15 21 views
0

L'implémentation java suivante du modèle de visiteur utilisant des génériques est-elle assez générale pour être utile? (Je suppose que c'est).Modèle de visiteur générique dans Java

Pourrait-il être amélioré d'une manière ou d'une autre? Il est important d'être facilement appelable en utilisant des classes anonymes. Merci.

(Exemple d'utilisation):

Vector<Number> numbers = new Vector<Number>(); 

     numbers.add(new Double(1.2)); 
     numbers.add(new Float(-1.2)); 
     numbers.add(new Double(4.8)); 
     numbers.add(new Float(-3.4)); 
     numbers.add(new Long(123456)); 
     numbers.add(new Short("14")); 

     For.each(numbers, new Visitor<Number>() { 
      public void doIt(Double n) { 
       System.out.println("doIt() for double: " + n); 
      } 
      public void doIt(Float n) { 
       System.out.println("doIt() for float: " + n); 
      } 
      public void doIt(Number n) { 
       System.out.println("doIt() for Number: " + n); 
      } 
     }); 

     Visitor<Number> visi = new Visitor<Number>() { 
      private StringBuffer all = new StringBuffer(); 
      public void doIt(Number n) { 
       System.out.println("doIt() for Number: " + n); 
       all.append(n.toString() + " "); 
      } 
      public Object getResult() { 
       return all; 
      } 
     }; 

     For.each(numbers, visi); 

     System.out.println ("all -> " + visi.getResult()); 

Définitions:

//............................................ 
abstract class Visitor<T> { 
    public void visit(T n) { 
     try { 
      this.getClass().getDeclaredMethod("doIt", n.getClass()).invoke(this, n); 
     } catch (Exception ex) { 
      doIt((T) n); 
     } 
    } 
    public void doIt(T n) { 
     System.out.println("doIt() for base " + n); 
    } 
    public Object getResult() { 
     return null; 
    } 
} // class 

//............................................ 
class For { 
    public static <T> void each (Collection<T> c, Visitor<T> f) { 
     for (T v : c) { 
      f.visit(v); 
     } 
    } //() 
} // class 
+3

comme donroby souligne que votre code ne correspond pas complètement au modèle de visiteur. Je pense qu'il y a plus de schéma de stratégie (les autres opinions peuvent varier). Votre code me rappelle Collections.sort (Liste, Comparateur) donc il peut avoir quelques informations utiles. – josefx

+1

Hier, j'ai vu un * excellent * exemple du modèle Visitor dans [la réponse d'aioobe] (http://stackoverflow.com/questions/3930808/is-there-a-better-option-for-this-code). Lisez toute la question, cela rendra tout le sens du motif. – BalusC

+0

@josefx La nouvelle version est-elle maintenant le modèle de visiteur? (Voir ci-dessous). – cibercitizen1

Répondre

2

Merci à la réponse de donroby au sujet de mon code initial ne pas mettre en œuvre le modèle de visiteur je suis venu à cette nouvelle version. Je suppose qu'il implémente désormais le modèle de visiteur sans avoir besoin de modifier l'élément visité avec une méthode accept(). Quoi qu'il en soit, il est capable d'appeler la bonne méthode en fonction du type d'élément (je suppose que c'est la mission de l'accept()), grâce à la réflexion.

D'abord, un exemple d'utilisation:

Vector<Number> numbers = new Vector<Number>(); 

    numbers.add(new Double(1.2)); 
    numbers.add(new Float(-1.2)); 
    numbers.add(new Double(4.8)); 
    numbers.add(new Float(-3.4)); 
    numbers.add(new Long(123456)); 
    numbers.add(new Short("14")); 

    For.each(numbers, new Visitor<Number>() { 
     public void doIt(Double n) { 
      System.out.println("doIt() for double: " + n); 
     } 
     public void doIt(Float n) { 
      System.out.println("doIt() for float: " + n); 
     } 
     public void doIt(Number n) { 
      System.out.println("doIt() for Number: " + n); 
     } 
    }); 

qui produit cette sortie

 
doIt() for double: 1.2 
doIt() for float: -1.2 
doIt() for double: 4.8 
doIt() for float: -3.4 
doIt() for Number: 123456 
doIt() for Number: 14 

Et enfin le code

abstract class Visitor<T> { 
public void visit(T n) { 
    try { 
     this.getClass().getDeclaredMethod("doIt", n.getClass()).invoke(this, n); 
    } catch (Exception ex) { 
     doIt((T) n); 
    } 
} 
public void doIt(T n) { 
    System.out.println("doIt() for base " + n); 
} 
public Object getResult() { 
    return null; 
} 

}

class For { 
public static <T> void each (Collection<T> c, Visitor<T> f) { 
    for (T v : c) { 
     f.visit(v); 
    } 
} //() 

}

+2

Oui ceci implémente le modèle de visiteur avec la réflexion au lieu de la double expédition. Un problème: la ligne doIt ((T) n) aura toujours pour résultat doIt (Object) étant appelé puisque le type générique sera effacé par le compilateur. – josefx

+1

Puisque vous avez demandé comment améliorer votre code, au lieu de stocker un résultat dans votre visiteur (votre premier exemple), vous pouvez utiliser une variable locale dans chaque() pour le stocker, le donner comme paramètre au visiteur et le retourner à la fin de chacun(). De cette façon, vous n'aurez aucun état dans le visiteur. – josefx

+0

@josefx peut-être qu'il me manque quelque chose mais doIt (Object) n'est pas appelé (afaik). Dans mon exemple Vector , doIt (Double) et doIt (Float) sont appelés pour les doubles et les flottants. doIt (Number) est appelé pour les autres types. – cibercitizen1

6

Ce n'est pas Visitor Pattern. Le visiteur est caractérisé par une méthode accept(Visitor v) qui interagit avec une méthode de visite chez le visiteur en prenant comme paramètre le visiteur et surchargé pour le type variable du visiteur, formant un mécanisme de «double expédition».

Je cite la section « Application » pour des visiteurs à Design Patterns:

Utilisez le modèle des visiteurs lorsque
  • une structure d'objet contient de nombreuses classes d'objets avec différentes interfaces et que vous souhaitez effectuer opérations sur ces objets que dépendent de leurs classes concrètes.
  • de nombreuses opérations distinctes et indépendantes doivent être effectuées sur objets dans une structure d'objet, et vous voulez éviter « polluer » leurs cours de ces opérations. Visitor permet de conserver les opérations associées en les définissant dans une classe. Lorsque la structure de l'objet est partagée par de nombreuses applications de , utilisez Visitor pour mettre les opérations dans les applications suivantes: qui en ont besoin. Les classes définissant la structure de l'objet changent rarement, mais vous voulez souvent définir de nouvelles opérations sur la structure . La modification des classes de structure d'objet nécessite redéfinissant l'interface pour tous les visiteurs , ce qui est potentiellement coûteux. Si les classes de la structure d'objet changent souvent, il est probablement préférable de définir les opérations dans ces classes.

Donc ce modèle est pour traiter opertaions similaires sur des objets de plusieurs types. Dans vos exemples, les objets que vous appelez les visiteurs ne peuvent traiter que d'un seul type. Dans votre réponse réviser pour utiliser la réflexion pour gérer plusieurs types (qui d'ailleurs serait mieux fait comme un edit à la question ou comme une question séparée), vous évitez de créer une méthode accept(Visitor v) dans les classes visitées par en utilisant la réflexion, qui est dans une certaine mesure en accomplissant le même but, mais quelque peu maladroitement. Je résisterais toujours à l'appeler une implémentation de Visitor.

Si le code dans le style que vous avez écrit ici est utile pour vous, utilisez-le, mais ne l'appelez pas Visiteur. Cela ressemble plus à un Strategy Pattern ou un Function Object, et si vous renommez la classe générique d'une manière qui reflète cela, c'est en fait utile, et votre utilisation est similaire aux modèles courants de gestion de liste dans les langages fonctionnels.

Ce que je probablement faire avec le code de la question est Renommez votre Visitor<T>-Operation<T> et Renommez votre visit(T t)-execute(T t) ou apply(T t), la pensée d'un Operation comme Function sans valeur de retour. En fait, j'ai utilisé exactement ceci d'une manière similaire à ce que vous faites, et j'ai utilisé des tactiques similaires pour la «cartographie» de collection en utilisant des objets Function<Domain, Range> génériques. Je ne suis pas sûr du nom du motif, mais ce n'est pas Visitor. Il apporte un style de compréhension de liste fonctionnelle à un monde OO où les fonctions ne sont pas naturellement des objets de première classe.

+0

De toute évidence, je dois accepter que je ne comprends pas le besoin d'une méthode accept() et de ce mécanisme de double expédition. Quel est l'avantage que nous obtenons? – cibercitizen1

+0

Je me corrige moi-même. Je vois que c'est une astuce de faire examiner les éléments visités par une fonction différente en fonction de son type. Mais il ne me semble pas très propre de devoir modifier les visites avec une méthode d'acceptation. De plus, cela n'est pas nécessaire si tous les éléments des collections sont vraiment du même type. – cibercitizen1

+0

@donroby La nouvelle version est-elle maintenant le modèle de visiteur? (Voir ci-dessous). – cibercitizen1

0

comment sur

for(String s : words) 
     System.out.println (s.toUpperCase()); 

    int total = 0; 
    for(String s : words) 
     total = total + s.length(); 
    System.out.println (" sum of lengths = " + total); 
+0

Simpler c'est mieux, mais j'étais intéressé par le modèle de visiteur. Merci. – cibercitizen1