Eine gute Möglichkeit, Klassen für komplexere Spielkartentypen als in einem Standarddeck zu erstellen?

9

Ich bin extrem neu in der objektorientierten Programmierung und versuche, mit einem einfachen Kartenspiel (wie es traditionell zu sein scheint!) In Python zu lernen. Ich habe das folgende Beispiel ausgeführt, das einwandfrei funktioniert, und mir beigebracht, wie Sie mehrere Instanzen der PlayingCard()Klasse erstellen, um eine Instanz der Klasse zu erstellen 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()



Ich möchte jetzt etwas mit komplexeren Karten machen, nicht nur mit einem Standard-52-Deck (das schön inkrementierende Werte hat). Das Deck, an das ich denke, ist das Monopoly-Kartenspiel:

Geben Sie hier die Bildbeschreibung ein

Es gibt 3 grundlegende Kartentypen: AKTIONSKARTEN, IMMOBILIENKARTEN und GELDKARTEN. Die Aktionskarten führen unterschiedliche Aktionen aus, die Eigenschaftskarten gehören zu verschiedenen Farbsätzen und die Geldkarten können unterschiedliche Werte haben. Zusätzlich können die Eigenschaftskarten "Platzhalter" sein und als Teil eines von zwei Sätzen verwendet werden. Schließlich hat jede Karte auch einen entsprechenden Geldwert (in der oberen Ecke jeder Karte angegeben). In den Mietaktionskarten kann die Karte nur für die auf der Karte angegebene Farbeigenschaft gelten.

Meine Frage ist nur allgemein, wie man mit einer solchen Situation umgeht und wie man diese verschiedenen Karten in ein klassenbasiertes Python-Programm aufnehmen kann. Sollte ich meine einzelne PlayingCard()Klasse behalten und nur viele Eingaben haben, wie z PlayingCard(type="PROPERTY", value="3M"). Oder wäre es besser, separate Klassen erstellen wie ActionPlayingCard(), PropertyPlayingCard(), etc? Oder gibt es einen besseren Weg? Wie gesagt, ich stehe hier am Anfang meines Lernens und wie man solche Situationen im Hinblick auf das Design auf höherer Ebene organisiert.

Danke vielmals.

Teeeee
quelle
Wenn Sie feststellen, dass die verschiedenen Kartentypen einige Funktionen gemeinsam haben, können Sie die Vererbung oder sogar eine abstrakte Klasse verwenden. Sie können das Factory-Muster lesen und verwenden, um den Kartentyp zu übergeben, und die entsprechende Klasse wird verwendet
Tomerikoo,
@Tomerikoo Vielen Dank, dass Sie darauf hingewiesen haben - ich habe ein wenig über das von Ihnen erwähnte Fabrikmuster gelesen. Soweit ich weiß, ist dies am nützlichsten, wenn Sie vorher nicht wissen, welche Objektklassen Sie erstellen müssen (möglicherweise nur zur Laufzeit). Da ich in diesem Fall jedoch weiß, wie das gesamte Deck aussehen sollte (wie viele von jedem Kartentyp, was sie jeweils tun usw.), gilt hier das Werksmuster?
Teeeeeee

Antworten:

3

Wenn Sie sich einem Problem mit OOP nähern , möchten Sie normalerweise Verhalten und Eigenschaften auf wiederverwendbare Weise modellieren, dh Sie sollten an Abstraktionen denken und Ihre Klassenhierarchie darauf basierend organisieren.

Ich würde so etwas wie folgendes schreiben:

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)

Da Python eine dynamisch typisierte Sprache ist, ist OOP meiner Meinung nach etwas schwieriger zu rechtfertigen, da wir uns nur auf Ententypisierung und dynamische Bindung verlassen können. Die Art und Weise, wie Sie Ihre Hierarchie organisieren, ist weniger wichtig.

Wenn ich dieses Problem beispielsweise in C # modellieren würde, würde ich ohne Zweifel die oben gezeigte Hierarchie verwenden, da ich mich auf Polymorphismus verlassen könnte , um verschiedene Typen darzustellen und den Fluss meiner Logik basierend auf dem zu analysierenden Kartentyp zu steuern.

Ein paar abschließende Bemerkungen:

  1. Python verfügt über sehr leistungsfähige integrierte Typen, aber die meiste Zeit erleichtert die Verwendung neuer benutzerdefinierter Typen, die darauf aufbauen, Ihr Leben.
  2. Sie müssen nicht von erben, objectda Typen in Python 3 (der einzige, der bis heute verwaltet wird) objectstandardmäßig von erben .

Aber am Ende des Tages gibt es keine perfekte Antwort. Der beste Weg wäre, beide Ansätze auszuprobieren und zu sehen, mit was Sie sich wohler fühlen.

Alex
quelle
7

Dies nennen wir "Designentscheidungen". Oft ist der "richtige" Weg Ansichtssache. Als Anfänger halte ich es für lehrreich, beide Implementierungen auszuprobieren, um zu sehen, wie sie funktionieren. Es wird Kompromisse geben, egal welchen Sie auswählen. Sie müssen entscheiden, welche dieser Kompromisse am wichtigsten sind. Wenn Sie diese Art von Entscheidungen treffen, werden Sie informiert, wenn Sie mehr Erfahrung sammeln.

Code-Lehrling
quelle
Ja, danke, ich mache genau das, was Sie sagen - ich suche nur nach Anleitung von Leuten, die erfahren genug sind, um instinktiv einen guten Ansatz zu kennen und hoffentlich zu verstehen, warum.
Teeeeeee
2

Sie könnten Vererbung verwenden. Hier erstellen Sie eine Hauptklasse und haben dann Unterklassen, die noch Funktionen und Werte aus der Mutterklasse enthalten. Sie können jedoch auch zusätzliche Werte und Funktionen für diese bestimmte Klasse haben.

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

Jetzt hat die iPhone-Klasse die gleichen Funktionen wie die Apple-Klasse und eine eigene Funktion. Wenn Sie mehr über die Vererbung erfahren möchten, empfehle ich Ihnen, Nachforschungen anzustellen.

MoriartyPy
quelle
Danke, ich verstehe die Vererbung und wie sie funktioniert. Ich bin jedoch mehr daran interessiert, wie es angesichts der Eigenschaften des Monopoly-Decks auf meine spezifische Situation angewendet werden kann.
Teeeeeee
@teeeeee Da jede Karte einen Wert hat, können Sie sie handeln und spielen. Sie können Funktionen / Prozeduren in einer Klasse erstellen, um diese Ereignisse zu behandeln, und dann zusätzliche Attribute und Funktionen für bestimmte Karten in einer Unterklasse haben.
MoriartyPy
0

Für das Monopol würde ich den Standpunkt der Spiellandung entwerfen. Keine Karten. Karten repräsentieren einfach die Landungen für die reale Welt.

Ali Berat Çetin
quelle
Bitte erläutern Sie.
Teeeeeee