Абстрактное наследование и дженерики (с игральными картами) - PullRequest
2 голосов
/ 18 марта 2009

Итак, я работал над некоторыми игральными картами на Java. (Не для каких-либо практических целей, на самом деле, мне просто нравится играть в карты, и они являются хорошей практикой). Сейчас, прямо сейчас, я делаю некоторые Карточные Структуры, колоды, руки, стопки и т. Д. Они все по сути одинаковые, Я полагаю, что хотел бы использовать наследство.

Проблема, с которой я столкнулся, состоит в том, что ядром каждой структуры является некоторый тип коллекции, но не все они используют один и тот же тип коллекции. Колода использует стек, так как колода чаще всего функционирует как стек. Но рука использует ArrayList (если есть что-то более эффективное, чем ArrayList для использования, это было бы также полезно знать). Поэтому, когда я пытаюсь написать абстрактный класс, я бы хотел избежать использования абстрактных методов (поскольку это противоречит первоначальной цели создания абстрактного класса для сохранения кода). Но все эти методы полагаются на базовую коллекцию по понятным причинам, но я не знаю, к какому типу относится коллекция. Это то, что я пробовал до сих пор:

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

Прежде всего, прости мои имена переменных. Я обычно называл все «theVariable», но решил, что использовать «el» было интереснее. (Вы должны как-то развлечься, верно?) Кроме того, использование защищенного было просто ради простоты.

Теперь, похоже, сработает общая идея: определить переменную в абстрактном классе, а затем определить ее псевдоним в дочернем классе, но я не уверен, что это хорошая практика. Реальная проблема у меня с конструктором. Конструктор в Pair, который принимает массив карт, не работает, потому что для добавления карт мне сначала нужно создать collection (в данном случае ArrayList) до того, как родительский конструктор попытается добавить карточки. Есть ли способ обойти это? Это даже жизнеспособный способ обработки наследства?

Ответы [ 4 ]

3 голосов
/ 18 марта 2009

Я думаю, что ваша самая большая проблема в том, что вы просто создаете эти классы без каких-либо реальных требований. Действительно ли наследство - правильный выбор? Такое ощущение, что вы разрабатываете классы, чтобы соответствовать заранее заданной реализации, а не наоборот.

Определите свои интерфейсы для каждого нужного вам класса на основе реальных требований, реализуйте их, а затем посмотрите, имеет ли смысл абстрактный базовый класс.

2 голосов
/ 18 марта 2009

Просто попросите каждую реализацию передать тип коллекции в конструкторе:

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

Возможно, вам придется немного поиграть с декларациями, но это должно вас видеть правильно

0 голосов
/ 18 марта 2009

У меня такое ощущение, что вы пытаетесь сделать две разные вещи с наследованием, поэтому это кажется запутанным -

С одной стороны, у вас есть концепция набора карт. Это будет коллекция карт. Итак, сначала мы знаем, что у нас есть это:

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

В этот момент ваши классы должны расходиться, потому что у нас пока есть степень общего поведения (ну, у нас, вероятно, будут некоторые дополнительные методы, такие как size (), nextCard (), isEmpty () и т. д., которые достаточно легко определить в защищенной коллекции, но не обращайте внимания на них сейчас).

Использовать собственный пример

 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 {
...
}

Здесь карточные наборы бывают разных видов. Они разделены, потому что они должны вести себя по-разному и представлять дополнительное поведение, которое имеют эти типы коллекций, из обобщенного понятия набора карт. Попытка вставить дополнительный код в абстрактный родительский объект может сэкономить несколько строк, но в конечном итоге приводит к запутыванию.

0 голосов
/ 18 марта 2009

Я дам быстрый (хакский?) Ответ: что вы могли бы сделать, это определить метод protected abstract Collection<Card> createCollection(), определенный в базовом классе, CardSet. Ваши подклассы переопределят это, чтобы создать и вернуть любой тип коллекции, подходящий для этого подкласса. Тогда конструктор суперкласса будет использовать этот метод для создания коллекции, после чего он может продолжить и добавить карточки:

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