вопрос о разработке абстрактного наследования на c #

#c# #inheritance #abstract

#c# #наследование #аннотация

Вопрос:

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

Мой вопрос таков: допустим, я хотел создать абстрактный базовый класс карт, из которого я мог бы составить любую карточную игру (например, давайте сравним базовую колоду игральных карт и Magic: The Gathering). Следуя этому: http://sourcemaking.com/refactoring/replace-type-code-with-class Мне удалось создать PokerCardType, который в основном представляет собой прославленное перечисление для каждой карты в стандартной колоде. Моя проблема возникает из-за того, как создать абстрактный класс Card, для которого требуется экземпляр CardType в нем, но конкретный экземпляр не определен. Моей первой мыслью было создать абстрактный класс CardType, который каким-то образом требовал бы набора статических объектов CardType для любого подкласса, но я не могу понять, как это сделать. Мой другой вопрос заключается в том, действительно ли эта идея имеет смысл, или я должен просто создать две отдельные структуры классов, одну для poker и одну для Magic. Если вам интересно, почему я хотел бы это сделать, это действительно просто для моего собственного образования. Спасибо за помощь или любые ссылки, которые вы можете предоставить.

Комментарии:

1. Я бы сначала спросил, какой общий набор «вещей» / атрибутов есть у Magic Cards с покерными картами?

2. Все ваши ответы в основном указывают на одну и ту же идею: есть ли достаточно общего между Магией и Покером, чтобы создать какой-то базовый объект, вероятно, нет. Прежде чем я начал все больше и больше углубляться в это, я думал, что лучшим способом было бы просто разделить их, но мир наследования быстро поглотил меня. Как предлагали многие, я собираюсь разделить их. Спасибо за все быстрые ответы. Любой из них можно было бы считать правильным ответом, но я даю его тейлонру, потому что он показал, как вы могли бы на самом деле это сделать, если бы это имело смысл. Еще раз спасибо.

Ответ №1:

На мой взгляд, вы на неверном пути. Следование принципам OO и написание хорошего кода имеют довольно много совпадений, но это никоим образом не одно и то же. Не пытайтесь следовать принципам OO, потому что кто-то вам так сказал. Следуйте им, потому что они соответствуют вашим потребностям в решении реальной проблемы. Обычная форма-> Круг / прямоугольник / …-> Пример рисования — это просто бессмыслица и сбивает людей с толку.

Сложно подвести итог, но я попытаюсь: для реализации некоторого абстрактного класса вам нужна хорошая абстракция. Что-то вроде «Card» не является абстракцией. Абстракцией будут операции, которые вы можете выполнять с картой. Эти операции, очевидно, зависят от контекста, поэтому в разных контекстах (программах) вы можете использовать разные хорошие абстракции для «карты».

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

Ответ №2:

Вы здесь переусердствовали с абстракцией. Если вы не разрабатываете чрезвычайно общий движок карточной игры (и я имею в виду чрезвычайно), я не вижу никаких причин, по которым колода покерных карт и колода волшебных карт могут рассматриваться как «is a Deck » для некоторого абстрактного класса Deck .

Просто начните с самой простой вещи, которая работает.

 class PokerDeck { }
class MagicDeck { }
  

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

Примечание: наследование — это, пожалуй, самая непонятая и неправильно используемая концепция ООП.

Комментарии:

1. Deck — плохой пример, потому что есть общая функциональность.

2. Да, годы спустя я все еще не могу понять, когда и как правильно использовать наследование : (

Ответ №3:

Вот пример колоды абстрактных классов с конкретными реализациями PokerDeck и MagicDeck. Как говорили другие, я не уверен, что это лучший пример, потому что на самом деле между играми не так много общего.

Может быть, если бы вы сделали что-то вроде раздачи с помощью SpadesHand, HeartsHand, PinochleHand, PresidentsHand, где у вас было бы по несколько карт в каждой раздаче. В каждой игре есть метод «PlayNextCard», и у них разные правила, но все они вращаются вокруг того, чтобы положить карту из вашей руки в общую стопку.

 public abstract class Deck
    {
          public int NumberOfCards{get; private set;}

          public IEnumerable<Card> Cards {get; private set;}

          public void Shuffle()
          {
              Cards = RandomizeCards();
          }

          protected abstract IEnumerable<Card> CreateCardList();

    }

    public class PokerDeck
    {
         public PokerDeck()
         {
              NumberOfCards = 52;
              Cards = CreateCardList();
         }
    }

    public class MagicDeck
    {
         public MagicDeck()
         {
             NumberOfCards = 10000; // have no idea
              Cards = CreateCardList();
         }
    }
  

Затем вы реализуете CreateCardList для каждого конкретного класса. Итак, в покере есть 2-A для червей, пик, треф и бубен. В Magic есть все, что есть в magic deck.

Вам не нужно реализовывать метод Shuffle, потому что он выполняется в базовом классе.

Ответ №4:

Похоже, вы хотите иметь статический класс CardType, который может быть определен с помощью набора экземпляров Card, которые вы хотите поддерживать. Если это недостаточно динамично для вас, это все равно может быть статический класс, который знает обо всех типах карт, которые были зарегистрированы в нем.

Ответ №5:

Я бы еще больше разбил требования. Что такого в картах из poker и magic, которые вы хотите представить с помощью базового класса? Помимо того, что они имеют размеры по высоте и ширине, у них очень мало общего в том, как они используются в соответствующих играх. Нельзя использовать покерные карты. Волшебные карты нельзя использовать для сборки флеш. Сами карты мало что делают.

Я полагаю, они могут перемещаться в случайном порядке… Если бы вы хотели смоделировать это, вы могли бы просто создать интерфейс IShuffleable, который могли бы реализовать оба класса.

Чтобы напрямую ответить на то, что, я думаю, вы спрашиваете, вы, вероятно, можете решить это с помощью дженериков. Допустим, вы хотите иметь колоду карт:

 public class CardDeck<T> : where T:BaseCardType
{
   private List<T> _cards new List<T>();

   public void Shuffle()
   {
      // blah blah
   }

   public void SomethingElseYouCanDoWithADeck
   { }
}
  

Я просто не думаю, что это что-то для вас сделает… Можете ли вы подробнее рассказать о том, как вы будете использовать эти классы после их разработки?

Ответ №6:

Идея абстрактных классов и наследования заключается в классах, которые близки к одному и тому же. Обычно это когда подкласс является «лучше определенным» или «более конкретным» экземпляром, чем просто базовый тип.

Например: полицейская машина — это более конкретная версия седана, поэтому полицейская машина будет наследоваться от седана. Но карта MTG не является более конкретным экземпляром покерной карты, и наоборот. Возможно, вам удастся сделать это для некоторых карточных игр, таких как карты для покера и Uno, потому что они имеют общие черты, такие как номинал и масть / цвет.