Uma boa maneira de fazer aulas para tipos de cartas de baralho mais complexos do que os encontrados em um baralho padrão?

9

Eu sou extremamente novo na programação orientada a objetos e estou tentando começar a aprender em python, criando um jogo de cartas simples (como parece ser tradicional!). Fiz o exemplo a seguir, que funciona bem, e me ensina a criar várias instâncias da PlayingCard()classe para criar uma instância da Deck()classe:

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()



Eu quero fazer algo agora com cartas mais complexas, não apenas com um baralho 52 padrão (que tem valores bem incrementados). O baralho que tenho em mente é o jogo de cartas Monopoly:

insira a descrição da imagem aqui

Existem três tipos fundamentais de cartões - cartões de AÇÃO, CARTÕES DE PROPRIEDADE e cartões de DINHEIRO. Os cartões de ação realizam ações diferentes, os cartões de propriedades pertencem a diferentes conjuntos de cores e os cartões de dinheiro podem ter valores diferentes. Além disso, os cartões de propriedades podem ser "curingas" e podem ser usados ​​como parte de um dos dois conjuntos. Finalmente, cada cartão também possui um valor monetário equivalente (indicado no canto superior de cada cartão). Nos cartões de ação de aluguel, o cartão só pode ser aplicado à propriedade de cor indicada no cartão.

Minha pergunta é geralmente como lidar com uma situação como essa, e qual seria uma boa maneira de incluir essas placas diferentes em um programa python baseado em classe? Devo manter minha PlayingCard()classe única e apenas ter muitas informações, como PlayingCard(type="PROPERTY", value="3M"). Ou seria melhor para criar classes separadas, como ActionPlayingCard(), PropertyPlayingCard(), etc? Ou há uma maneira melhor? Como eu disse, estou no começo do meu aprendizado aqui e como organizar esses tipos de situações em termos de design de nível superior.

Muito Obrigado.

teeeeee
fonte
Se você achar que os diferentes tipos de cartões compartilham alguns recursos, você pode usar herança ou mesmo uma classe Abstrata. Você pode ler sobre e usar o Padrão de Fábrica para passar o tipo de cartão e a classe apropriada será usada
Tomerikoo 18/02
@Tomerikoo Obrigado por apontar isso - li um pouco sobre o padrão de fábrica que você mencionou. Pelo que entendi, isso é mais útil quando você não sabe de antemão quais classes de objetos você precisará criar (talvez apenas sabendo em tempo de execução). No entanto, como neste caso eu sei como deve ser a aparência de todo o baralho (quantos de cada tipo de cartão, o que cada um deles faz etc.), o padrão de fábrica é aplicável aqui?
teeeeee 19/02

Respostas:

3

Quando você está abordando um problema com OOP , geralmente deseja modelar comportamentos e propriedades de maneira reutilizável, ou seja, deve pensar em abstrações e organizar sua hierarquia de classes com base nisso.

Eu escreveria algo como o seguinte:

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)

Como o Python é uma linguagem de tipo dinâmico, o POO é um pouco mais difícil de justificar na minha opinião, já que podemos confiar apenas na tipagem do pato e na ligação dinâmica , a maneira como você organiza sua hierarquia é menos importante.

Se eu modelasse esse problema em C #, por exemplo, sem dúvida usaria a hierarquia mostrada acima, porque poderia confiar no polimorfismo para representar tipos diferentes e orientar o fluxo da minha lógica com base no tipo de cartão que está sendo analisado.

Algumas considerações finais:

  1. O Python possui tipos internos muito poderosos, mas na maioria das vezes o uso de novos tipos personalizados criados a partir deles facilita sua vida.
  2. Você não precisa herdar de, objectuma vez que os tipos no Python 3 (que é o único mantido até hoje) são herdados objectpor padrão.

Mas, no final do dia, não há uma resposta perfeita, a melhor maneira seria tentar ambas as abordagens e ver com o que você está mais confortável.

alex
fonte
7

Isso é o que chamamos de "decisões de design". Muitas vezes, o caminho "correto" é uma questão de opinião. Como iniciante, acho que seria instrutivo tentar ambas as implementações para ver como elas funcionam. Haverá compensações, não importa qual você escolher. Você precisa decidir quais dessas trocas são mais importantes. A tomada desse tipo de decisão será informada à medida que você ganha mais experiência.

Code-Apprentice
fonte
Sim, obrigado, estou fazendo exatamente o que você diz - apenas procurando orientação de pessoas com experiência suficiente para saber instintivamente uma boa abordagem e, com sorte, entender o porquê.
teeeeee 18/02
2

Você poderia usar herança. É aqui que você cria uma classe principal e, em seguida, tem subclasses que ainda contêm funções e valores da classe mãe, mas também podem ter valores e funções extras para essa classe específica.

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

Agora, a classe iPhone tem as mesmas funções que a classe Apple e sua própria função. Se você deseja aprender mais sobre herança, recomendo fazer uma pesquisa.

MoriartyPy
fonte
Obrigado, entendo a herança e como ela funciona. No entanto, estou mais interessado em como isso poderia ser aplicado à minha situação específica, dadas as propriedades que o convés do monopólio possui.
teeeeee 25/02
@teeeeee Como cada cartão tem um valor, você pode trocá-los e reproduzi-los, você pode criar funções / procedimentos em uma classe para lidar com esses eventos e, em seguida, ter atributos e funções extras para determinados cartões em uma subclasse.
MoriartyPy
0

Para monopólio, eu projetaria o ponto de vista dos desembarques do jogo. Não cartões. Os cartões simplesmente representam os desembarques para o mundo real.

Ali Berat Çetin
fonte
Por favor elabore.
teeeeee 28/02