There is never one "right" or "wrong" way to do something in programming. Just ways which do or do not work for you, your team and your project. So don't worry about doing things the right way, worry about writing a program you can still understand, change and extend later.
The pattern from the question has some implications for later maintainability. When each card is a static
, they seem more like card-types to me than actual instances of cards. This conceptual difference might not matter much when each card only exists once in the game, but will matter when you allow multiple cards of the same type.
With this pattern, you can write code like if (lastCard == Card.PIG) sayOink();
. But because the ==
-operator only checks if both references point to the same object, this only works when lastCard is a reference to Card.PIG
. There might be multiple pig-cards in the game, but they are all represented by references to the one Card.PIG
object. That means if Card.PIG
has any internal state, that state will be shared by all pig-cards in the game.
When your game doesn't have state for individual cards, that's fine.
But if you have some mechanics where cards have a state (like counters on cards, a "tapping" mechanic like in some trading card games, etc...) or when you want cards themselves to be aware of their current location in the game (in the deck, in a player's hand, on the table), this is not going to work. You will then need an instantiated object for each card.
If you want to follow that architecture, you have two different classes Card
and CardType
. When there are 7 pig-cards in the game, you would have one CardType PIG
and 7 instances of Card
which each have a reference to PIG
which you can pass to it in its constructor. Your code to initialize your deck of cards could look something like this:
static CardType DOG = new CardType(image, a, b, ...);
static CardType SHEEP = new CardType(image, a, b, ...);
static CardType PIG = new CardType(image, a, b, ...);
static int NUM_DOG_CARDS_PER_DECK = 3;
static int NUM_PIG_CARDS_PER_DECK = 7;
static int NUM_SHEEP_CARDS_PER_DECK = 7;
[...]
int i = 0;
for (i = 0; i < NUM_DOG_CARDS_PER_DECK; i++) {
deck.add(new Card(DOG));
}
for (i = 0; i < NUM_PIG_CARDS_PER_DECK; i++) {
deck.add(new Card(PIG));
}
for (i = 0; i < NUM_SHEEP_CARDS_PER_DECK; i++) {
deck.add(new Card(SHEEP));
}
But there is always more than one way. Instead of having a CardType
class, you could implement each card type as a separate class which inherits from Card
. Then your code would look like this:
for (i = 0; i < NUM_DOG_CARDS_PER_DECK; i++) {
deck.add(new DogCard());
}
for (i = 0; i < NUM_PIG_CARDS_PER_DECK; i++) {
deck.add(new PigCard());
}
for (i = 0; i < NUM_SHEEP_CARDS_PER_DECK; i++) {
deck.add(new SheepCard());
}
The differences between different card types (like their names and images) would then be implemented in DogCard
, SheepCard
etc.. This architecture would be most appropriate if your card types don't just differ by the values of their variables, but also have some mechanical differences which require card-specific code. That code would then belong into those sub-classes.