¿Una buena forma de hacer clases para tipos de naipes más complejos que los que se encuentran en un mazo estándar?


9

Soy extremadamente nuevo en la programación orientada a objetos, y estoy tratando de comenzar a aprender en Python haciendo un juego de cartas simple (¡como parece ser tradicional!). He hecho el siguiente ejemplo, que funciona bien, y me enseña a hacer varias instancias de la PlayingCard()clase para crear una instancia de la Deck()clase:

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



Quiero hacer algo ahora con cartas más complejas, no solo un mazo estándar de 52 (que tiene valores que se incrementan muy bien). El mazo que tengo en mente es el juego de cartas Monopoly:

ingrese la descripción de la imagen aquí

Hay 3 tipos fundamentales de tarjetas: tarjetas de ACCIÓN, TARJETAS DE PROPIEDAD y tarjetas de DINERO. Las tarjetas de acción realizan diferentes acciones, las tarjetas de propiedad pertenecen a diferentes conjuntos de colores y las tarjetas de dinero pueden tener diferentes valores. Además, las tarjetas de propiedad pueden ser "comodines" y pueden usarse como parte de uno de los dos conjuntos. Finalmente, cada tarjeta también tiene un valor monetario equivalente (indicado en la esquina superior de cada tarjeta). En las tarjetas de acción de alquiler, la tarjeta solo puede aplicarse a la propiedad de color indicada en la tarjeta.

Mi pregunta es, en general, cómo manejar una situación como esta, y ¿cuál sería una buena manera de incluir estas diferentes tarjetas en un programa de Python basado en clases? ¿Debo mantener mi PlayingCard()clase individual y solo tener muchas entradas, como PlayingCard(type="PROPERTY", value="3M"). ¿O sería mejor crear clases separadas tales como ActionPlayingCard(), PropertyPlayingCard(), etc.? ¿O hay un mejor camino? Como digo, estoy al comienzo de mi aprendizaje aquí, y cómo organizar este tipo de situaciones en términos del diseño de nivel superior.

Muchas gracias.


Si encuentra que los diferentes tipos de tarjetas comparten algunas características, puede usar la herencia, o incluso una clase abstracta. Puede leer y usar el Patrón de fábrica para poder pasar el tipo de tarjeta y se usará la clase apropiada
Tomerikoo

@Tomerikoo Gracias por señalarlo: he leído un poco sobre el patrón de fábrica que mencionó. Según tengo entendido, esto es más útil cuando no sabe de antemano qué clases de objetos necesitará crear (quizás solo sepa en tiempo de ejecución). Sin embargo, dado que en este caso sé cómo debería ser toda la baraja (¿cuántos de cada tipo de tarjeta, qué hacen, etc.), es aplicable aquí el patrón de fábrica?
teeeeee

Respuestas:


3

Cuando se acerca a un problema con la POO , generalmente desea modelar el comportamiento y las propiedades de una manera reutilizable, es decir, debe pensar en abstracciones y organizar su jerarquía de clases en función de eso.

Escribiría algo como lo siguiente:

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)

Debido a que Python es un lenguaje de tipo dinámico, OOP es un poco más difícil de justificar en mi opinión, ya que solo podemos confiar en la tipificación de pato y el enlace dinámico , la forma en que organiza su jerarquía es menos importante.

Si tuviera que modelar este problema en C #, por ejemplo, sin duda usaría la jerarquía mostrada anteriormente, porque podría confiar en el polimorfismo para representar diferentes tipos y guiar el flujo de mi lógica según el tipo de tarjeta que se está analizando.

Un par de observaciones finales:

  1. Python tiene tipos incorporados muy potentes, pero la mayoría de las veces el uso de nuevos tipos personalizados que se basan en ellos hace que su vida sea más fácil.
  2. No tiene que heredar objectya que los tipos en Python 3 (que es el único que se mantiene hasta hoy) heredan de objectforma predeterminada.

Pero, al final del día, no hay una respuesta perfecta, la mejor manera sería probar ambos enfoques y ver con qué se siente más cómodo.


7

Esto es lo que llamamos "decisiones de diseño". A menudo, la forma "correcta" es una cuestión de opinión. Como principiante, creo que sería instructivo probar ambas implementaciones para ver cómo funcionan. Habrá compensaciones sin importar cuál elija. Tienes que decidir cuáles de esas compensaciones son más importantes. La toma de este tipo de decisiones será informada a medida que gane más experiencia.


Sí, gracias, estoy haciendo exactamente lo que usted dice: solo busco orientación de personas con la experiencia suficiente para conocer instintivamente un buen enfoque y, con suerte, entender por qué.
teeeeee

2

Podrías usar la herencia. Aquí es donde crea una clase principal y luego tiene subclases que aún contienen funciones y valores de la clase madre; sin embargo, también pueden tener valores y funciones adicionales para esa clase 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

Ahora la clase de iPhone tiene las mismas funciones que la clase de Apple y su propia función. Si desea obtener más información sobre la herencia, le recomiendo que investigue un poco.


Gracias, entiendo la herencia y cómo funciona. Sin embargo, estoy más interesado en cómo podría aplicarse a mi situación específica, dadas las propiedades que tiene la baraja Monopoly.
teeeeee

@teeeeee Como cada carta tiene un valor, puedes intercambiarlas y jugarlas, puedes crear funciones / procedimientos en una clase para manejar estos eventos y luego tener atributos y funciones adicionales para ciertas cartas en una subclase.
MoriartyPy

0

Por monopolio, diseñaría el punto de vista de los juegos. No tarjetas Las cartas simplemente representan los aterrizajes para el mundo real.


Por favor elabora.
teeeeee
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.