I have structured a package containing interfaces that could be implemented and used for any card game.
I'm interested in knowing if I'm on the right track OOP-wise, or If I need to change my design.
ICard.java
package com.tn.deck;
public interface ICard {
void printCard();
}
IDeck.java
package com.tn.deck;
public interface IDeck {
ICard dealCard();
void shuffle();
}
IHand.java
package com.tn.deck;
public interface IHand {
void addCard(ICard card);
void removeCard(ICard card);
void showHand();
int calculateValue();
}
IRank.java
package com.tn.deck;
public interface IRank {
int[] addRanks();
}
ISuit.java
package com.tn.deck;
public interface ISuit {
String[] addSuits();
}
Here is an example usage of the interfaces when I want to create a game of BlackJack:
Card.java
package com.tn.blackjack;
import com.tn.deck.ICard;
public class Card implements ICard {
private String suit;
private int rank;
Card(String suit, int rank) {
this.suit = suit;
this.rank = rank;
}
public String getSuit() {
return suit;
}
public int getRank() {
return rank;
}
@Override
public void printCard() {
System.out.printf("%s%d ", suit, rank);
}
}
Deck.java
package com.tn.blackjack;
import com.tn.deck.ICard;
import com.tn.deck.IDeck;
import java.util.ArrayList;
import java.util.List;
public class Deck implements IDeck {
private int sizeOfDeck;
private List<ICard> cards;
Deck(int sizeOfDeck) {
this.sizeOfDeck = sizeOfDeck;
this.cards = new ArrayList<>(sizeOfDeck);
}
void initializeWith(String[] suits, int[] ranks) {
for(int i = 0; i < sizeOfDeck; i++) {
cards.add(new Card(
suits[i % suits.length],
ranks[i % ranks.length]
));
}
print();
}
public int getSize() {
return cards.size();
}
private void print() {
for(ICard card : cards) {
card.printCard();
}
}
@Override
public ICard dealCard() {
if(cards.size() < 1) {
throw new IllegalArgumentException("The deck is empty");
}
int index = new Random().nextInt(cards.size());
ICard card = cards.get(index);
cards.remove(index);
return card;
}
@Override
public void shuffle() {
Collections.shuffle(cards);
}
}
Player.java
package com.tn.blackjack;
import com.tn.deck.ICard;
import com.tn.deck.IHand;
import java.util.ArrayList;
import java.util.List;
public class Player implements IHand{
private int id;
private int numberOfCardsAllowed;
private List<ICard> cards;
Player(int id, int numberOfCardsAllowed) {
this.id = id;
this.numberOfCardsAllowed = numberOfCardsAllowed;
this.cards = new ArrayList<>(numberOfCardsAllowed);
}
public int getId() {
return id;
}
@Override
public void addCard(ICard card) {
if(cards.size() > numberOfCardsAllowed) {
throw new IllegalArgumentException("You are not allowed to draw more cards");
}
cards.add(card);
}
@Override
public void removeCard(ICard card) {
}
@Override
public void showHand() {
for(ICard card : cards) {
card.printCard();
}
}
@Override
public int calculateValue() {
int score = 0;
for(ICard card : cards) {
score += ((Card)card).getRank();
}
return score;
}
}
BlackJack.java
package com.tn.blackjack;
import com.tn.deck.ICard;
import com.tn.deck.IRank;
import com.tn.deck.ISuit;
public class BlackJack implements ISuit, IRank {
private static int DECK_SIZE = 52;
private static int MAX_NUMBER_OF_CARDS = 22;
private String[] suits;
private int[] ranks;
private Deck deck;
private Player[] players;
public BlackJack() {
this.suits = addSuits();
this.ranks = addRanks();
this.deck = new Deck(DECK_SIZE);
this.players = new Player[] {
new Player(1, MAX_NUMBER_OF_CARDS),
new Player(2, MAX_NUMBER_OF_CARDS)
};
deck.initializeWith(suits, ranks);
}
public ICard dealCard() {
return deck.dealCard();
}
@Override
public String[] addSuits() {
return new String[] {"\u2660", "\u2665", "\u2666", "\u2663"};
}
@Override
public int[] addRanks() {
return new int[] {2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 11};
}
}
BlackJack
class serves as an example of the intended usage of theDeck
. \$\endgroup\$(0, 0), (1, 1), (2, 2), (3, 3), (0, 4), (1, 5), (2, 6)...
It completes the card deck as the 13 ranks shifts suitable in relation with the 4 suits. \$\endgroup\$