2009-03-18 3 views
2

Итак, я работал над некоторыми игральными картами на Java. (На самом деле не для практической цели, мне просто нравятся игральные карты, и они - хорошая практика) Теперь, сейчас, я делаю несколько карточных структур, колод, рук, свай и т. Д. Все они практически одинаковы, поэтому Я полагаю, что хотел бы использовать некоторое наследование.Abstract Inheritance and Generics (с игральными картами)

Проблема, с которой я столкнулся, состоит в том, что ядром каждой структуры является некоторый тип коллекции, но они не все используют один и тот же тип коллекции. В колоде используется Stack, поскольку колода по большей части функционирует как стек чаще всего. Но в руке используется 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, который принимает массив карт, не работает, потому что для добавления карт мне сначала нужно создать коллекцию (в данном случае ArrayList), прежде чем родительский конструктор попытается добавить карты. Есть ли способ обойти это? Это даже жизнеспособный способ обработки наследства?

ответ

2

Просто каждый реализации проходит вниз тип коллекции в конструкторе:

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

Мне пришлось изменить это (новый ArrayList ()) на этот ((C) новый ArrayList ()), но из-за этого я получаю ошибку «непроверенные или небезопасные операции». – Retsam

+0

True - я обновился, чтобы создать статическую фабрику, которая вернет безопасный объект типа. – Stephen

+0

Вы можете заставить его вернуть общий тип списка, но я верю, что в этом случае вы хотите оставить свои варианты открытыми. Я также рискну предположить, что наличие пустого конструктора имеет ограниченную выгоду (когда вы создаете свой специалист/класс удобства в любом случае) – Stephen

0

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

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

Итак, я думаю, что ответ Стивена, вероятно, лучше ... но для чего это стоит, это дизайн дизайна, который пригодился мне несколько раз. –

0

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

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

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

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

+0

Я посмотрел на наборы, но проблема с наборами заключается в том, что у вас не может быть повторяющихся элементов, и поскольку карты равны (по моему определению), если они имеют один и тот же набор и значение, это может быть проблемой. (Если вы играете в игру с несколькими колодами) – Retsam

3

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

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

+0

Ну, я пришел к согласию использовать реферат, потому что тратил много времени на повторное определение повторяющихся методов. Я имею в виду, если мы реалистичны, какая разница между кучей и колодой? ну, куча лицевой стороной вверх. Вот и все. – Retsam

+0

Это моя мысль. Если бы вы на самом деле что-то кодировали, вам даже понадобился бы отдельный класс для кучи против колоды? Я не просто дым здесь, я на самом деле написал карточный код и код на основе домино. Я обнаружил, что легко переоценить, если вы не фокусируетесь на требованиях. – JohnOpincar

+0

+1 для Джона. Сначала напишите код клиента, позволяя ему зависеть от интерфейса, который имеет только те методы, которые ему нужны. У разных зависимостей, вероятно, будут разные интерфейсы. Затем вы можете увидеть, сколько различных реализаций вам действительно нужно. – erickson

 Смежные вопросы

  • Нет связанных вопросов^_^