2009-03-18 7 views
2

J'ai donc travaillé sur quelques cartes à jouer en Java. (Pas vraiment pour des raisons pratiques, j'aime juste jouer aux cartes et c'est une bonne pratique) Maintenant, en ce moment, je fais des structures de cartes, des decks, des mains, des piles, etc. Ils sont tous essentiellement les mêmes, donc Je me dis que j'aimerais utiliser un peu d'héritage.Résumé Hérédité et génériques (avec cartes à jouer)

Le problème que j'ai rencontré est que le noyau de chaque structure est un type de collection, mais ils n'utilisent pas tous le même type de collection. Un deck utilise une pile, car un deck fonctionne essentiellement comme une pile le plus souvent. Mais, une main utilise une ArrayList (S'il y a quelque chose de plus efficace qu'une ArrayList à utiliser, cela serait bon à savoir aussi). Ainsi, en essayant d'écrire une classe abstraite, je voudrais éviter d'utiliser des méthodes abstraites (car cela va à l'encontre de l'objectif initial de créer une classe abstraite, de conserver du code). Mais, toutes ces méthodes s'appuient sur la collection de base, pour des raisons évidentes, mais je ne sais pas quel type est la collection. C'est ce que j'ai essayé jusqu'à présent:

public abstract class CardSet 
{ 
    protected AbstractCollection<Card> elCollection; 
    public CardSet() 
    { 
     super(); 
    } 
    public CardSet(Card[] cards) 
    { 
     super(); 
     for(Card c: cards) 
      elCollection.add(c); 

    } 
    public void add(Card c) 
    { 
     elCollection.add(c); 
    } 
} 
public class Pair extends CardSet //Pair is just a dummy class to get inheritance right 
{ 
    ArrayList<Card> elPair; 
    public Pair() 
    { 
     elPair = new ArrayList<Card>(); //elPair is defined, because casting 
     elCollection = elPair; // elCollection to arraylist would be a pain. 

    } 
    public Pair(Card[] cards) 
    { this(); 
     super(cards); 
    } 
} 

Tout d'abord, pardonnez mes noms de variables. J'avais l'habitude de nommer tout "theVariable", mais j'ai décidé que l'utilisation de "el" était plus intéressante. (Vous devez vous amuser d'une façon ou d'une autre, n'est-ce pas?) En outre, utiliser protégé était juste pour des raisons de simplicité. Maintenant, l'idée générale semble fonctionner, définir une variable dans la classe abstraite, puis en définir un alias dans la classe enfant, mais je ne suis pas sûr que ce soit une bonne pratique. Le vrai problème que j'ai est avec le constructeur. Le constructeur dans Pair qui accepte un tableau de cartes ne fonctionne pas, car pour ajouter les cartes, je dois d'abord créer la collection (dans ce cas, ArrayList) avant que le constructeur parent essaie d'ajouter les cartes. Y a-t-il un moyen de contourner ce problème? Est-ce même une façon viable de gérer l'héritage?

Répondre

2

Juste ont chaque implémentation passer sur le type de collection dans le constructeur:

public abstract class CardSet<C extends Collection<Card>> 
{ 
    protected final C elCollection; 
    public CardSet<Collection<Card>> cardSet() 
    { 
     return new CardSet<Collection<Card>>(new ArrayList<Card>()); 
    } 
    public CardSet(C col){ 
     this.elCollection = col; 
    } 
    public CardSet(C col, Card[] cards) 
    { 
     this(col); 
     for(Card c: cards) 
      elCollection.add(c); 
    } 
    public void add(Card c) 
    { 
     elCollection.add(c); 
    } 
} 
public class Pair extends CardSet<List<Card>> 
{ 
    public Pair() 
    { 
     super(new ArrayList<Card>()); 

    } 
    public Pair(Card[] cards) 
    { 
     super(new ArrayList<Card>(), cards); 
    } 
} 

Vous pourriez avoir à jouer un peu avec les déclarations, mais si vous voyez à droite

+0

Je devais changer cela (new ArrayList ()) à ceci ((C) new ArrayList ()), mais j'obtiens maintenant une erreur "opérations non vérifiées ou dangereuses" à cause de cela. – Retsam

+0

True - J'ai mis à jour pour créer une fabrique statique qui retournera un objet de type safe. – Stephen

+0

Vous pourriez le faire retourner un type générique de liste, mais je crois que dans ce cas vous voulez laisser vos options ouvertes. Je suppose également que le fait d'avoir un constructeur vide est un avantage limité (quand vous créez de toute façon vos classes de spécialistes/commodités) – Stephen

0

Je vais donner une réponse rapide (hackish?): quelque chose que vous pourriez faire est d'avoir une méthode protected abstract Collection<Card> createCollection() définie dans la classe de base, CardSet. Vos sous-classes remplaceraient ceci pour créer et retourner n'importe quel type de collection approprié pour cette sous-classe. Ensuite, le constructeur superclasse utiliserait cette méthode pour créer la collection, après quoi il pourrait aller de l'avant et ajouter les cartes:

public CardSet(Card[] cards) { 
    super(); 
    self.elCollection = createCollection(); 
    Collections.addAll(self.elCollection, cards); 
} 
+0

Donc je pense que la réponse de Stephen est probablement meilleure ... mais pour ce que ça vaut, c'est un motif de conception qui m'a été utile à quelques reprises. –

0

Mon sentiment est que vous essayez de faire 2 choses distinctes avec l'héritage, il semble confus -

D'une part, vous avez le concept d'un jeu de cartes. Cela aura une collection de cartes. Alors d'abord, nous savons que nous avons ceci:

public abstract class CardSet { 
    protected Collection<Card> cards; 
} 

A ce stade, vos classes devrait diverger, parce que ce que nous avons à ce jour est l'étendue du comportement commun (bien, nous aurons probablement quelques méthodes supplémentaires là, comme size(), nextCard(), isEmpty(), etc etc, qui sont assez faciles à définir sur la collection protégée, mais ça ne les dérange pas maintenant).

Pour utiliser votre exemple

public class Deck extends CardSet { 
     public Deck(){ 
     cards = new Stack<Card>(); 
    } 

    public void shuffle() { ... } 
} 

public class Hand extends CardSet { 
    public Hand(){ 
     //i'm using a set here instead of the list in your example because I 
     // don't think ordering is a property of a hand. 
     cards = new HashSet<Card>(); 
    } 
} 
public class Pair extends CardSet { 
... 
} 

Ici cardsets sont de différents types. Ils sont séparés parce qu'on s'attend à ce qu'ils se comportent différemment, et représentent le comportement additionnel que ces types de collections ont de la notion généralisée d'un jeu de cartes. Essayer de pousser le code supplémentaire dans le parent abstrait peut sauver quelques lignes mais finalement obfusactes.

+0

J'ai regardé des ensembles, mais le problème avec les ensembles est que vous ne pouvez pas avoir d'éléments dupliqués, et comme les cartes sont égales (selon ma définition) si elles ont la même suite et la même valeur, cela peut poser problème. (Si vous jouez à un jeu avec plusieurs jeux) – Retsam

3

Je pense que votre plus gros problème est que votre création que ces classes sans aucune exigence réelle. L'héritage est-il vraiment le bon choix ici? Il semble que vous concevez les classes pour les adapter à une mise en œuvre préconçue au lieu de l'inverse.

Définissez vos interfaces pour chaque classe dont vous avez besoin en fonction des besoins réels, les mettre en œuvre, puis voir si une classe de base abstraite est logique.

+0

Eh bien, je suis venu à la concussion d'utiliser un résumé parce que je passais beaucoup de temps à redéfinir les méthodes en double. Je veux dire, si nous sommes réalistes, quelle est la différence entre une pile et une terrasse? Eh bien, une pile est face visible. C'est à peu près ça. – Retsam

+0

C'est mon point. Si vous étiez en train de coder quelque chose, auriez-vous même besoin d'une classe séparée pour une pile par rapport à une pile? Je ne suis pas juste en train de souffler de la fumée ici, j'ai déjà écrit une carte et un code basé sur le domino. Ce que j'ai trouvé, c'est qu'il est facile de sur-conception si vous ne vous concentrez pas sur les exigences. – JohnOpincar

+0

+1 pour John. Écrivez le code client en premier, en le laissant dépendre d'une interface qui a juste les méthodes dont il a besoin. Différentes dépendances auront probablement des interfaces différentes. Ensuite, vous pouvez voir combien d'implémentations différentes vous avez vraiment besoin. – erickson