2010-10-15 6 views
0

Imaginez le scénario suivant.Collections de différents types hérités d'un seul objet par rapport à une seule collection de super objet

class Shape 
{ 
    // methods of Class Shape 
} 

class Circle extends Shape 
{ 
    // methods of Class Shape 
    // methods of Class Circle 
} 

class Square extends Shape 
{ 
    // methods of Class Shape 
    // methods of Class Square 
} 

class Canvas // This class should a bucket of objects that belong to Square and Circle 
{ 
    // Declaring a single list of supertype 
    List<Shape> heterogeneousCollection; 

    // Declaring two separate lists 
    List<Cirlce> homogeneousCollection; 
    List<Square> homogeneousCollection; 
} 

Le schéma relationnel correspondant est comme suit

  • Table Shape contient des informations maître
  • Tableaux Cercle & carrés viennent enrichir les données en forme par un moyen de référence clé étrangère (Cercle & - Square sont disjoints ensembles avec des colonnes différentes)
  • Tableau Le canevas est une sorte d'agrégation de données
  • Cercle (1..n cardinalité)
  • Canvas rejoint la place (1..n cardinalité)

Quelle est la meilleure approche « Déclaration d'une liste unique de super-type (collection hétérogène) » ou « Déclarant deux listes distinctes (Deux collections homogènes différentes) "

Je considère les points suivants pour prendre une décision.

  1. Comment peupler un objet Canvas lors de la lecture d'une base de données?

Tenir compte d'une manière d'éviter N + 1 problème et la lecture à partir d'une seule requête quelque chose comme

SELECT * FROM 
Canvas INNER JOIN Circle ON .. 
Canvas INNER JOIN Square ON .. 
Circle INNER JOIN Shape ON .. 
Square INNER JOIN Shape ON .. 

Maintenant, pour chaque enregistrement de toile nous nous retrouvons avec des lignes (B + C). Cependant, avec plusieurs outils ORM, il est possible de regrouper des ensembles de données distincts dans Circle et Square en deux listes distinctes. (Je considère iBatis ici)

  1. Comment gérons-nous plusieurs fonctions sur ces objets?
    Imaging un cas où nous prévoyons de gérer une fonction d'interface utilisateur pour afficher des données dans l'objet Canvas. En dehors des fonctions communes entre Circle et Square, chacun d'eux peut avoir des fonctions différentes. Par exemple, Square peut avoir getLengthOfSide(), alors que Circle peut avoir getRadius(). Si nous utilisons une liste hétérogène, nous pourrions finir par utiliser un opérateur de cast à chaque endroit où nous avons besoin d'accéder à ces fonctions.

    Class Canvas 
    { 
        void drawAll() 
        { 
         for (Shape s : heterogeneousCollection) 
         { 
          if (s instanceof Circle) 
          { 
           s.draw(); // common function present in Shape also 
           s.xyz(); // specific to Circle 
          } 
          if (s instanceof Square) 
          { 
           s.draw(); // common function present in Shape also 
           s.abc(); // specific to Square 
          } 
         } 
        } 
    } 
    

En cas de deux listes homogènes que nous pourrions avoir deux différents pour les boucles pour chaque liste séparément. Cependant, si nous avons besoin d'ajouter un nouveau type de forme (disons triangle), cela affecte Canvas, ce qui, je pense, est le résultat d'un défaut de conception et Java pourrait être équipé pour faire face à cela. Veuillez jeter un peu de lumière à ce sujet. Toute référence à des livres/liens serait d'une grande aide. Je voulais juste vous dire que ce n'est pas une mission scolaire et que je cherche sérieusement différentes solutions. Pardonnez-moi pour une longue question. PS: Une autre solution List<? extends Shape> est exclue car nous ne pouvons pas insérer d'objets dans cette collection.

+0

Correction: Dans la boucle, une distribution des fonctions d'appel précède l'interface utilisateur. Cercle c = (Cirle) s; c.draw(); c.abc(); ... – Gopal

+0

Je ne comprends pas votre remarque "PS". –

Répondre

0

Si l'on suppose qu'il ya un certain nombre d'endroits où vous devez légitimement traiter le traitement de cercle et la place indépendamment, je traiterait de la collection hétérogène en appliquant la Visitor Pattern

Ceci a l'avantage que si vous ajoutez Triangle plus tard, puis lorsque vous ajoutez une méthode T visitTriangle(Triangle triangle); au visiteur, votre code ne compilera pas jusqu'à ce que vous mettiez à jour chaque visiteur, en évitant les mauvaises surprises d'exécution.

Cependant ... si vous ne parlez que d'une seule instance de traitement de Circle et Square différemment, alors l'application Visitor ici est exagérée et je voudrais simplement ajouter une méthode abstraite à Shape pour doSomeSpecificUiThing().

Il ressemblerait à quelque chose comme ceci:

class ShapeVisitor<T> 
{ 
    T visitCircle(Circle circle); 
    T visitSquare(Square square); 
} 

class Shape 
{ 
    abstract <T> T accept(ShapeVisitor<T> visitor); 

    // methods of Class Shape 
} 

class Circle extends Shape 
{ 
    <T> T accept(ShapeVisitor<T> visitor) { 
     return visitor.visitCircle(this); 
    } 

    // methods of Class Circle 
} 

class Square extends Shape 
{ 
    <T> T accept(ShapeVisitor<T> visitor) { 
     return visitor.visitSquare(this); 
    } 

    // methods of Class Square 
} 

Class Canvas 
{ 
    void drawAll() 
    { 
     for (Shape s : heterogeneousCollection) 
     { 
      s.draw(); 
      s.accept(new ShapeVisitor<Void>() { 
       @Override Void visitCircle(Circle circle) { 
        circle.xyz(); 
        return null; 
       } 

       @Override Void visitSquare(Square square) { 
        square.abc(); 
        return null; 
       } 
      } 
     } 
    } 
} 
+0

Merci beaucoup pour votre réponse. Je vais essayer d'en savoir plus sur le parcours des visiteurs. – Gopal

+0

Ok, maintenant que j'ai eu une bonne réponse, je voudrais partager le fait qu'iBatis supporte la cartographie de l'héritage. Ceci est possible en utilisant les attributs "discriminator" et "subtype" d'un resultMap. Une lecture attentive à la documentation peut être plus utile. – Gopal

1

Il est difficile de donner des solutions lorsque nous avons des informations générales plutôt que des faits spécifiques. Exemple: les méthodes xyz() et abc().Que sont-ils et pourquoi? Dans votre exemple, ils semblent avoir une utilisation similaire, donc je considérerais de définir une méthode abstraite dans Shape appelée doSomethingSpecific() afin qu'elle soit implémentée par toutes les sous-classes Shape. Maintenant, votre code est:

void drawAll() 
     { 
     for (Shape s : heterogeneousCollection) 
      { 
      s.draw();    // common function present in Shape also 
      s.doSomethingSpecific(); // specific to each implementation 
      } 
     } 

Je préfère les collections hétérogènes lorsque cela est possible. Je n'aime pas beaucoup instanceof pour la raison même que vous signalez - que se passe-t-il lorsque nous ajoutons Triangle au mélange.

+0

Merci beaucoup pour votre réponse. – Gopal