Хороший способ сделать классы для более сложных типов игральных карт, чем в стандартной колоде?

9

Я чрезвычайно новичок в объектно-ориентированном программировании и пытаюсь начать обучение на python с создания простой карточной игры (как кажется, традиционной!). Я сделал следующий пример, который отлично работает, и учит меня создавать несколько экземпляров PlayingCard()класса для создания экземпляра Deck()класса:

class PlayingCard(object):
    def __init__(self, suit, val):
        self.suit = suit
        self.value = val

    def print_card(self):
        print("{} of {}".format(self.value, self.suit))

class Deck(object):
    def __init__(self):
        self.playingcards = []
        self.build()

    def build(self):
        for s in ["Spades", "Clubs", "Diamonds", "Hearts"]:
            for v in range(1,14):
                self.playingcards.append(PlayingCard(s,v))

deck = Deck()



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

введите описание изображения здесь

Существует 3 основных типа карт - карты ACTION, PROPERTY CARDS и карты MONEY. Карты действий выполняют разные действия, карты свойств принадлежат разным наборам цветов, а карты денег могут иметь разные значения. Кроме того, карты свойств могут быть «подстановочными знаками» и могут использоваться как часть одного из двух наборов. Наконец, каждая карта также имеет эквивалентную денежную стоимость (указывается в верхнем углу каждой карты). В арендных картах действия карта может применяться только к цвету, указанному на карте.

Мой вопрос заключается в том, как справиться с подобной ситуацией, и как было бы неплохо включить эти разные карты в программу на языке Python? Должен ли я сохранить свой единственный PlayingCard()класс, и просто иметь много входов, таких как PlayingCard(type="PROPERTY", value="3M"). Или было бы лучше создать отдельные классы, такие как ActionPlayingCard(), PropertyPlayingCard()и т. Д.? Или есть лучший способ? Как я уже сказал, я нахожусь в начале своего обучения здесь, и как организовать эти типы ситуаций с точки зрения дизайна более высокого уровня.

Большое спасибо.

teeeeee
источник
Если вы обнаружите, что разные типы карт имеют некоторые общие функции, вы можете использовать наследование или даже класс Abstract. Вы можете прочитать и использовать Factory Pattern, чтобы передать тип карты и использовать соответствующий класс
Томерику
@Tomerikoo Спасибо, что указали на это - я прочитал немного о фабричном образце, который вы упомянули. Насколько я понимаю, это наиболее полезно, когда вы не знаете заранее, какие классы объектов вам нужно будет создать (возможно, только зная во время выполнения). Тем не менее, поскольку в этом случае я знаю, как должна выглядеть вся колода (сколько карт каждого типа, что они делают и т. Д.), Применима ли здесь фабричная схема?
Teeeeee

Ответы:

3

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

Я бы написал что-то вроде следующего:

class Card:
    def __init__(self, money_value=0):
        self.money_value = money_value

class ActionCard(Card):
    def __init__(self, action, money_value=0):
        super().__init__(money_value=money_value)

        self.action = action

class RentActionCard(ActionCard):
    def __init__(self, action, color, money_value=0):
        super().__init__(action, money_value=money_value)

        self.color = color

    def apply(self, property_card):
        if property_card.color != self.color:
            # Don't apply
        # Apply

class PropertyCard(Card):
    def __init__(self, color, money_value=0):
        super().__init__(money_value=money_value)

        self.color = color

class WildcardPropertyCard(PropertyCard):
    def __init__(self, color, money_value=0):
        super().__init__(color, money_value=money_value)

class MoneyCard(Card):
    def __init__(self, money_value=0):
        super().__init__(money_value=money_value)

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

Например, если бы я смоделировал эту проблему в C # , я бы без сомнения использовал иерархию, показанную выше, потому что я мог бы полагаться на полиморфизм для представления различных типов и управления потоком моей логики в зависимости от того, какой тип карты анализируется.

Пара заключительных замечаний:

  1. Python имеет очень мощные встроенные типы, но в большинстве случаев использование новых пользовательских типов, основанных на них, облегчает вашу жизнь.
  2. Вам не нужно наследовать, objectтак как типы в Python 3 (который является единственным поддерживаемым на сегодняшний день) наследуются objectпо умолчанию.

Но, в конце концов, нет идеального ответа, лучший способ - попробовать оба подхода и посмотреть, что вам удобнее.

Алекс
источник
7

Это то, что мы называем «дизайнерские решения». Часто «правильный» путь - это вопрос мнения. Как новичок, я думаю, что было бы полезно попробовать обе реализации, чтобы увидеть, как они работают. Там будут компромиссы независимо от того, какой вы выберете. Вы должны решить, какие из этих компромиссов являются наиболее важными. Принятие такого рода решений будет проинформировано, когда вы приобретете больше опыта.

Код-Apprentice
источник
Да, спасибо, я делаю именно так, как вы говорите - просто ищу руководство от людей, которые достаточно опытны, чтобы инстинктивно знать хороший подход и, надеюсь, понять, почему.
Тееееее
2

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

class Apple:
    def __init__(self, yearMade):
        pass

    def ring(self):
        print('ring ring')

class iPhone(Apple):
    def __init__(self, number)
        number = number

    def func():
        pass

Теперь класс iPhone имеет те же функции, что и класс Apple, и свою собственную функцию. Если вы хотите узнать больше о наследовании, я рекомендую сделать некоторые исследования.

MoriartyPy
источник
Спасибо, я понимаю наследование и как оно работает. Меня больше интересует, как это можно применить к моей конкретной ситуации, учитывая свойства, которыми обладает колода Монополия.
Тееееее
@teeeeee Поскольку у каждой карты есть значение, вы можете торговать ими и играть в них, вы можете создавать функции / процедуры в классе для обработки этих событий, а затем иметь дополнительные атрибуты и функции для определенных карт в подклассе.
MoriartyPy
0

Для монополии я бы разработал точку зрения на игру. Не карты. Карты просто представляют приземления для реального мира.

Али Берат Четин
источник
Пожалуйста, дополните.
Тееееее