#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, потому что они имеют общие черты, такие как номинал и масть / цвет.