I am extremely new to object-oriented programming, and am trying to begin learning in python by making a simple card game (as seems to be traditional!). I have done the following example which works fine, and teaches me about making multiple instances of the PlayingCard()
class to create an instance of the Deck()
class:
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()
There are 3 fundamental types of cards - ACTION cards, PROPERTY cards, and MONEY cards. The action cards perform different actions, the property cards belong to different colour sets, and the money cards can have different values. Additionally, the property cards can be "wildcards", and can be used as part of one of two sets. Finally, every card also has an equivalent money value (indicated in the top corner of each card). In the rent action cards, the card can only apply to the colour property indicated on the card.
My question is just generally how to handle a situation like this, and what would be a nice way to include these different cards in a class-based python program? Should I keep my single PlayingCard()
class, and just have many inputs, such as PlayingCard(type="PROPERTY", value="3M")
. Or would it be better to create seperate classes such as ActionPlayingCard()
, PropertyPlayingCard()
, etc ? Or is there a better way? As I say, I am at the beginning of my learning here, and how to organise these types of situations in terms of the higher level design.
Many thanks.
When you are approaching a problem with OOP, you usually want to model behavior and properties in a reusable way, i.e., you should think of abstractions and organize your class hierarchy based on that.
I would write something like the following:
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)
Due to Python being a dynamically typed language, OOP is a little harder to justify in my opinion, since we can just rely on duck typing and dynamic binding, the way you organize your hierarchy is less important.
If I were to model this problem in C# for example, I would without a doubt use the hierarchy showed above, because I could rely on polymorphism to represent different types and guide the flow of my logic based on what type of card is being analyzed.
A couple of final remarks:
object
since types in Python 3 (which is
the only one maintained as of today) inherit from object
by default.But, at the end of the day, there isn't a perfect answer, the best way would be to try both of the approaches and see what you're more comfortable with.