My First Deck of Cards

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
6
down vote

favorite












I'm looking for an honest review of what I've got thus far. I'm still trying to get my head wrapped around software development and have picked Java as my starting language. That said, I figured that I would attempt to create a reusable module for which I'd use this as an external library.



I think it would be invaluable to hear from you experts on where I can improve or refactor. The code here is just snippets. Full code on GitHub.



Card.java



package com.frijolie.cards;

public interface Card extends Comparable<Card>

// returns the value of the Card. inherently coupled with the Rank
int getValue();

// returns the Rank of the Card. Values from Ace,2,3..., Jack,Queen,King
Rank getRank();

// returns the Suit of the Card. Values are Diamonds, Hearts, Spades, Clubs
Suit getSuit();

// returns the color of the Card. Values are Red or Black
Color getColor();

// display the card onscreen
void displayCard();

// compares this Card to another, returns if they are equal
boolean equals(final Object o);

// Generates a unique int value of a Card
int hashCode();

// flips a Card faceUp or faceDown
void flip();

// returns true if this Card is faceUp
boolean isFaceUp();




Rank.java



package com.frijlie.cards;

public enum Rank

ACE("Ace","A",1),
TWO("Two","2",2),
THREE("Three","3",3),
... // snipped for brevity
JACK("Jack","J",10),
QUEEN("Queen","Q",10),
KING("King","K","10);

private final String name;
private final String letter;
private final int value;

Rank(final String name, final String letter, final int value)
this.name = name;
this.letter = letter;
this.value = value;


... // accessor methods




Suit.java



package com.frijolie.cards;

public enum Suit
CLUBS("Clubs",'u2663', Color.BLACK),
DIAMONDS("Diamonds",'u2666', Color.RED),
SPADES("Spades",'u2660', Color.BLACK),
HEARTS("Hearts",'u2764', Color.RED);

private final String name;
private final char symbol;
private final Color color;

Suit(final String name, final char symbol, final Color color)
this.name = name;
this.symbol = symbol;
this.color = color;


... // accessor methods



Color.java



package com.frijolie.cards;

public enum Color
RED("Red"),
BLACK("Black");

private final String name;

Color(final String name)
this.name = name;


public final String getName()
return name;




Deck.java



package com.frijolie;

// snipped imports

public class Deck

private boolean isShuffled;
private Stack<Card> deck;

// default constructor, will create a single deck of 52 cards
public Deck()
this(1)


// overloaded constructor, will create multiple decks of 52 cards
public deck(int numberOfDecks)
deck = new Stack<>();
clearDeck();
for (int i = 0; i < numberOfDecks; i++)
populateDeck();



// a method to shuffle the collection of cards. default will shuffle 1x
public void shuffle()
shuffle(1);


// overloaded shuffle method. will shuffle the deck a number of times.
public void shuffle(int numberOfTimes)
isShuffled = true;
for (int i = 0; i < numberOfTimes; i++)
Collections.shuffle(deck);



// returns true if the deck has been shuffled
public boolean isShuffled()
return isShuffled;


// returns the number of cards remaining in the deck
public int numberOfCards()
return deck.size();


// a method to remove a card from the deck
public Card removeCard(Card card)
if(deck.contains(Objects.requireNonNull(card)))
int index = deck.indexOf(card);
return deck.remove(index);
else
return null;



// returns true if the specified card is contained in the deck
public boolean containsCard(Card card)
return deck.contains(Objects.requireNonNull(card));


// populates the deck with all possible cards in the deck. 52 cards
public void populateDeck()
for(Suit suit: Suit.values())
for(Rank rank: Rank.values())
deck.add(new PlayingCard(rank,suit));




// Overloaded method which allows any number of decks of 52 cards.
public void populateDeck(int numberOfDecks)
for(int i = 0; i < numberOfDecks; i++)
populateDeck();



// remove all cards from the deck
public void clearDeck()
deck.clear();


// returns an unmodifiable collection, a view, of the deck.
public Collection<Card> getUnmodifiableCollection()
return new ArrayList<>(deck);


// returns the first card in the collection.
public Card pop()
return deck.pop();




Hand.java



package com.frijolie.cards;

// snipped imports

public class Hand implements Comparable<Hand>

private List<Card> cards;

// default constructor
public Hand()
cards = new ArrayList<>();


// will add a specific card to the Hand
public void addCard(Card card)
cards.add(Objects.requireNonNull(card));


// will remove a specific card from the Hand if it's contained in the Hand
public Card removeCard(Card card)
if (cards.contains(Objects.requireNonNull(card)))
int index = cards.indexOf(card);
return cards.remove(index);
else
throw new IllegalArgumentException("This card did not exist in the hand. It cannot be removed.");



// method to display all the Cards in the Hand
public void displayHand()
for (Card card : cards)
if (card.isFaceUp())
card.displayCard();
else
System.out.println("[ Card Back ]");




// returns the sum of the value of all Cards in the Hand
public int calculateValue()
var handValue = 0;
for (Card card : cards)
handValue += card.getRank().getValue();

return handValue;


// remove all cards in the Hand
public void clearHand()
cards.clear();


// returns a unmodifiable collection, a view, of all cards in the Hand
public Collection<Card> getUnmodifiableCollection()
return Collections.unmodifiableCollection(cards);


// compares one Hand to another
public int compareTo(final Hand otherHand)
return this.calculateValue() - otherHand.calculateValue();


// sorts cards in the Hand by Suit. Doesn't factor Rank within the Suit when sorting
public void sortyBySuit()
cards.sort(Comparator.comparing(Card::getSuit));


// sorts cards in the Hand by Rank. Doesn't factor Suit when sorting.
public void sortByRank()
cards.sort(Comparator.comparing(Card::getRank));


// sorts cards in the Hand by Color. Doesn't factor Suit or Rank when sorting.
public void sortByColor()
cards.sort(Comparator.comparing(Card::getColor));


// sorts cards in the Hand by Suit then Rank in ascending order.
public void sort()
cards.sort((Card first, Card second) -> first.compareTo(second));


// allows a card in the hand to be flipped in the opposite orientation
public void flipCard(int index)
cards.get(index).flip();




PlayingCard.java



package com.frijolie.cards;

import java.util.Objects;

public class PlayingCard implements Card

private final Rank rank;
private final Suit suit;
private boolean isFaceUp;

// default constructor
public PlayingCard(final Rank rank, final Suit suit)
this.rank = rank;
this.suit = suit;
isFaceUp = true;


... // snipping implemented accessor methods from Card interface

// returns a string representation of the card
@Override
public String toString()
return rank.getLetter() + suit.getSymbol();


// compares one PlayingCard to another
@Override
public boolean equals(final Object o)
Objects.requireNonNull(o);
boolean isEqual;

if (!(o instanceof Card))
isEqual = false;
else if (this == 0)
isEqual = true;
else
var card = (Card) o;
isEqual = (this.getRank().equals(card.getRank()) && this.suit.equals(card.getSuit()));

return isEqual;


// generates unique int value of a card
@Override
public int hashCode()
int suitMultiplier = suit.ordinal() + 2;
int rankMultiplier = rank.ordinal() + 3;
return (suitMultiplier * rankMultiplier) * 31;


// flip the card in the opposite orientation, faceUp/faceDown
@Override
public void flip()
isFaceUp = !isFaceUp;


// returns true if the card is faceUp
@Override
public boolean isFaceUp()
return isFaceUp;


// compares one card to another and returns an integer value
@Override
public int compareTo(Card o)
var card = Objects.requireNonNull(o);
var suitCompareResult = this.suit.compareTo(card.getSuit());
if (suitCompareResult != 0)
return suitCompareResult;
else
return this.rank.compareTo(card.getRank());





What could I improve?



I've toyed with the idea to make Hand and/or Deck an interface. However, I opted to go with classes in favor of code reuse and contained behavior. I send unmodifiable collections (Hand, Deck) as I want to control the behavior of the internal collection within their respective classes. You can see what it looks like, but you can't modify the Deck or Hand outside the class. Is this a good defensive strategy?



I'm also not 100% comfortable with the requirement of a Rank and Suit. How about a Magic the Gathering Card? That doesn't have a suit, but you could argue that it has a rank. Is it better to leave the implementation up to the other developer? Same could be said for an Uno card, Color.Red and Color.BLACK aren't the only options there.



I've thought about the Rank enum also. If the game you model has an ACE as a high card (not low as supplied here), how would you fix that in your own implementation? Create a new AceHighRank and declare them in a different order? However, that would break PlayingCard because it's expecting a Rank (not AceHighRank). Right?







share|improve this question

















  • 3




    "Go easy on me" … "I look forward to your honest review. I have thick skin. I can take it." — Which do you want?
    – 200_success
    May 10 at 19:08










  • Touché. Updated.
    – Frijolie
    May 10 at 19:12










  • Nothing wrong with being self-taught.
    – The Mattbat999
    May 10 at 19:26






  • 2




    Welcome to Code Review! Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
    – Malachi♦
    May 10 at 19:45
















up vote
6
down vote

favorite












I'm looking for an honest review of what I've got thus far. I'm still trying to get my head wrapped around software development and have picked Java as my starting language. That said, I figured that I would attempt to create a reusable module for which I'd use this as an external library.



I think it would be invaluable to hear from you experts on where I can improve or refactor. The code here is just snippets. Full code on GitHub.



Card.java



package com.frijolie.cards;

public interface Card extends Comparable<Card>

// returns the value of the Card. inherently coupled with the Rank
int getValue();

// returns the Rank of the Card. Values from Ace,2,3..., Jack,Queen,King
Rank getRank();

// returns the Suit of the Card. Values are Diamonds, Hearts, Spades, Clubs
Suit getSuit();

// returns the color of the Card. Values are Red or Black
Color getColor();

// display the card onscreen
void displayCard();

// compares this Card to another, returns if they are equal
boolean equals(final Object o);

// Generates a unique int value of a Card
int hashCode();

// flips a Card faceUp or faceDown
void flip();

// returns true if this Card is faceUp
boolean isFaceUp();




Rank.java



package com.frijlie.cards;

public enum Rank

ACE("Ace","A",1),
TWO("Two","2",2),
THREE("Three","3",3),
... // snipped for brevity
JACK("Jack","J",10),
QUEEN("Queen","Q",10),
KING("King","K","10);

private final String name;
private final String letter;
private final int value;

Rank(final String name, final String letter, final int value)
this.name = name;
this.letter = letter;
this.value = value;


... // accessor methods




Suit.java



package com.frijolie.cards;

public enum Suit
CLUBS("Clubs",'u2663', Color.BLACK),
DIAMONDS("Diamonds",'u2666', Color.RED),
SPADES("Spades",'u2660', Color.BLACK),
HEARTS("Hearts",'u2764', Color.RED);

private final String name;
private final char symbol;
private final Color color;

Suit(final String name, final char symbol, final Color color)
this.name = name;
this.symbol = symbol;
this.color = color;


... // accessor methods



Color.java



package com.frijolie.cards;

public enum Color
RED("Red"),
BLACK("Black");

private final String name;

Color(final String name)
this.name = name;


public final String getName()
return name;




Deck.java



package com.frijolie;

// snipped imports

public class Deck

private boolean isShuffled;
private Stack<Card> deck;

// default constructor, will create a single deck of 52 cards
public Deck()
this(1)


// overloaded constructor, will create multiple decks of 52 cards
public deck(int numberOfDecks)
deck = new Stack<>();
clearDeck();
for (int i = 0; i < numberOfDecks; i++)
populateDeck();



// a method to shuffle the collection of cards. default will shuffle 1x
public void shuffle()
shuffle(1);


// overloaded shuffle method. will shuffle the deck a number of times.
public void shuffle(int numberOfTimes)
isShuffled = true;
for (int i = 0; i < numberOfTimes; i++)
Collections.shuffle(deck);



// returns true if the deck has been shuffled
public boolean isShuffled()
return isShuffled;


// returns the number of cards remaining in the deck
public int numberOfCards()
return deck.size();


// a method to remove a card from the deck
public Card removeCard(Card card)
if(deck.contains(Objects.requireNonNull(card)))
int index = deck.indexOf(card);
return deck.remove(index);
else
return null;



// returns true if the specified card is contained in the deck
public boolean containsCard(Card card)
return deck.contains(Objects.requireNonNull(card));


// populates the deck with all possible cards in the deck. 52 cards
public void populateDeck()
for(Suit suit: Suit.values())
for(Rank rank: Rank.values())
deck.add(new PlayingCard(rank,suit));




// Overloaded method which allows any number of decks of 52 cards.
public void populateDeck(int numberOfDecks)
for(int i = 0; i < numberOfDecks; i++)
populateDeck();



// remove all cards from the deck
public void clearDeck()
deck.clear();


// returns an unmodifiable collection, a view, of the deck.
public Collection<Card> getUnmodifiableCollection()
return new ArrayList<>(deck);


// returns the first card in the collection.
public Card pop()
return deck.pop();




Hand.java



package com.frijolie.cards;

// snipped imports

public class Hand implements Comparable<Hand>

private List<Card> cards;

// default constructor
public Hand()
cards = new ArrayList<>();


// will add a specific card to the Hand
public void addCard(Card card)
cards.add(Objects.requireNonNull(card));


// will remove a specific card from the Hand if it's contained in the Hand
public Card removeCard(Card card)
if (cards.contains(Objects.requireNonNull(card)))
int index = cards.indexOf(card);
return cards.remove(index);
else
throw new IllegalArgumentException("This card did not exist in the hand. It cannot be removed.");



// method to display all the Cards in the Hand
public void displayHand()
for (Card card : cards)
if (card.isFaceUp())
card.displayCard();
else
System.out.println("[ Card Back ]");




// returns the sum of the value of all Cards in the Hand
public int calculateValue()
var handValue = 0;
for (Card card : cards)
handValue += card.getRank().getValue();

return handValue;


// remove all cards in the Hand
public void clearHand()
cards.clear();


// returns a unmodifiable collection, a view, of all cards in the Hand
public Collection<Card> getUnmodifiableCollection()
return Collections.unmodifiableCollection(cards);


// compares one Hand to another
public int compareTo(final Hand otherHand)
return this.calculateValue() - otherHand.calculateValue();


// sorts cards in the Hand by Suit. Doesn't factor Rank within the Suit when sorting
public void sortyBySuit()
cards.sort(Comparator.comparing(Card::getSuit));


// sorts cards in the Hand by Rank. Doesn't factor Suit when sorting.
public void sortByRank()
cards.sort(Comparator.comparing(Card::getRank));


// sorts cards in the Hand by Color. Doesn't factor Suit or Rank when sorting.
public void sortByColor()
cards.sort(Comparator.comparing(Card::getColor));


// sorts cards in the Hand by Suit then Rank in ascending order.
public void sort()
cards.sort((Card first, Card second) -> first.compareTo(second));


// allows a card in the hand to be flipped in the opposite orientation
public void flipCard(int index)
cards.get(index).flip();




PlayingCard.java



package com.frijolie.cards;

import java.util.Objects;

public class PlayingCard implements Card

private final Rank rank;
private final Suit suit;
private boolean isFaceUp;

// default constructor
public PlayingCard(final Rank rank, final Suit suit)
this.rank = rank;
this.suit = suit;
isFaceUp = true;


... // snipping implemented accessor methods from Card interface

// returns a string representation of the card
@Override
public String toString()
return rank.getLetter() + suit.getSymbol();


// compares one PlayingCard to another
@Override
public boolean equals(final Object o)
Objects.requireNonNull(o);
boolean isEqual;

if (!(o instanceof Card))
isEqual = false;
else if (this == 0)
isEqual = true;
else
var card = (Card) o;
isEqual = (this.getRank().equals(card.getRank()) && this.suit.equals(card.getSuit()));

return isEqual;


// generates unique int value of a card
@Override
public int hashCode()
int suitMultiplier = suit.ordinal() + 2;
int rankMultiplier = rank.ordinal() + 3;
return (suitMultiplier * rankMultiplier) * 31;


// flip the card in the opposite orientation, faceUp/faceDown
@Override
public void flip()
isFaceUp = !isFaceUp;


// returns true if the card is faceUp
@Override
public boolean isFaceUp()
return isFaceUp;


// compares one card to another and returns an integer value
@Override
public int compareTo(Card o)
var card = Objects.requireNonNull(o);
var suitCompareResult = this.suit.compareTo(card.getSuit());
if (suitCompareResult != 0)
return suitCompareResult;
else
return this.rank.compareTo(card.getRank());





What could I improve?



I've toyed with the idea to make Hand and/or Deck an interface. However, I opted to go with classes in favor of code reuse and contained behavior. I send unmodifiable collections (Hand, Deck) as I want to control the behavior of the internal collection within their respective classes. You can see what it looks like, but you can't modify the Deck or Hand outside the class. Is this a good defensive strategy?



I'm also not 100% comfortable with the requirement of a Rank and Suit. How about a Magic the Gathering Card? That doesn't have a suit, but you could argue that it has a rank. Is it better to leave the implementation up to the other developer? Same could be said for an Uno card, Color.Red and Color.BLACK aren't the only options there.



I've thought about the Rank enum also. If the game you model has an ACE as a high card (not low as supplied here), how would you fix that in your own implementation? Create a new AceHighRank and declare them in a different order? However, that would break PlayingCard because it's expecting a Rank (not AceHighRank). Right?







share|improve this question

















  • 3




    "Go easy on me" … "I look forward to your honest review. I have thick skin. I can take it." — Which do you want?
    – 200_success
    May 10 at 19:08










  • Touché. Updated.
    – Frijolie
    May 10 at 19:12










  • Nothing wrong with being self-taught.
    – The Mattbat999
    May 10 at 19:26






  • 2




    Welcome to Code Review! Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
    – Malachi♦
    May 10 at 19:45












up vote
6
down vote

favorite









up vote
6
down vote

favorite











I'm looking for an honest review of what I've got thus far. I'm still trying to get my head wrapped around software development and have picked Java as my starting language. That said, I figured that I would attempt to create a reusable module for which I'd use this as an external library.



I think it would be invaluable to hear from you experts on where I can improve or refactor. The code here is just snippets. Full code on GitHub.



Card.java



package com.frijolie.cards;

public interface Card extends Comparable<Card>

// returns the value of the Card. inherently coupled with the Rank
int getValue();

// returns the Rank of the Card. Values from Ace,2,3..., Jack,Queen,King
Rank getRank();

// returns the Suit of the Card. Values are Diamonds, Hearts, Spades, Clubs
Suit getSuit();

// returns the color of the Card. Values are Red or Black
Color getColor();

// display the card onscreen
void displayCard();

// compares this Card to another, returns if they are equal
boolean equals(final Object o);

// Generates a unique int value of a Card
int hashCode();

// flips a Card faceUp or faceDown
void flip();

// returns true if this Card is faceUp
boolean isFaceUp();




Rank.java



package com.frijlie.cards;

public enum Rank

ACE("Ace","A",1),
TWO("Two","2",2),
THREE("Three","3",3),
... // snipped for brevity
JACK("Jack","J",10),
QUEEN("Queen","Q",10),
KING("King","K","10);

private final String name;
private final String letter;
private final int value;

Rank(final String name, final String letter, final int value)
this.name = name;
this.letter = letter;
this.value = value;


... // accessor methods




Suit.java



package com.frijolie.cards;

public enum Suit
CLUBS("Clubs",'u2663', Color.BLACK),
DIAMONDS("Diamonds",'u2666', Color.RED),
SPADES("Spades",'u2660', Color.BLACK),
HEARTS("Hearts",'u2764', Color.RED);

private final String name;
private final char symbol;
private final Color color;

Suit(final String name, final char symbol, final Color color)
this.name = name;
this.symbol = symbol;
this.color = color;


... // accessor methods



Color.java



package com.frijolie.cards;

public enum Color
RED("Red"),
BLACK("Black");

private final String name;

Color(final String name)
this.name = name;


public final String getName()
return name;




Deck.java



package com.frijolie;

// snipped imports

public class Deck

private boolean isShuffled;
private Stack<Card> deck;

// default constructor, will create a single deck of 52 cards
public Deck()
this(1)


// overloaded constructor, will create multiple decks of 52 cards
public deck(int numberOfDecks)
deck = new Stack<>();
clearDeck();
for (int i = 0; i < numberOfDecks; i++)
populateDeck();



// a method to shuffle the collection of cards. default will shuffle 1x
public void shuffle()
shuffle(1);


// overloaded shuffle method. will shuffle the deck a number of times.
public void shuffle(int numberOfTimes)
isShuffled = true;
for (int i = 0; i < numberOfTimes; i++)
Collections.shuffle(deck);



// returns true if the deck has been shuffled
public boolean isShuffled()
return isShuffled;


// returns the number of cards remaining in the deck
public int numberOfCards()
return deck.size();


// a method to remove a card from the deck
public Card removeCard(Card card)
if(deck.contains(Objects.requireNonNull(card)))
int index = deck.indexOf(card);
return deck.remove(index);
else
return null;



// returns true if the specified card is contained in the deck
public boolean containsCard(Card card)
return deck.contains(Objects.requireNonNull(card));


// populates the deck with all possible cards in the deck. 52 cards
public void populateDeck()
for(Suit suit: Suit.values())
for(Rank rank: Rank.values())
deck.add(new PlayingCard(rank,suit));




// Overloaded method which allows any number of decks of 52 cards.
public void populateDeck(int numberOfDecks)
for(int i = 0; i < numberOfDecks; i++)
populateDeck();



// remove all cards from the deck
public void clearDeck()
deck.clear();


// returns an unmodifiable collection, a view, of the deck.
public Collection<Card> getUnmodifiableCollection()
return new ArrayList<>(deck);


// returns the first card in the collection.
public Card pop()
return deck.pop();




Hand.java



package com.frijolie.cards;

// snipped imports

public class Hand implements Comparable<Hand>

private List<Card> cards;

// default constructor
public Hand()
cards = new ArrayList<>();


// will add a specific card to the Hand
public void addCard(Card card)
cards.add(Objects.requireNonNull(card));


// will remove a specific card from the Hand if it's contained in the Hand
public Card removeCard(Card card)
if (cards.contains(Objects.requireNonNull(card)))
int index = cards.indexOf(card);
return cards.remove(index);
else
throw new IllegalArgumentException("This card did not exist in the hand. It cannot be removed.");



// method to display all the Cards in the Hand
public void displayHand()
for (Card card : cards)
if (card.isFaceUp())
card.displayCard();
else
System.out.println("[ Card Back ]");




// returns the sum of the value of all Cards in the Hand
public int calculateValue()
var handValue = 0;
for (Card card : cards)
handValue += card.getRank().getValue();

return handValue;


// remove all cards in the Hand
public void clearHand()
cards.clear();


// returns a unmodifiable collection, a view, of all cards in the Hand
public Collection<Card> getUnmodifiableCollection()
return Collections.unmodifiableCollection(cards);


// compares one Hand to another
public int compareTo(final Hand otherHand)
return this.calculateValue() - otherHand.calculateValue();


// sorts cards in the Hand by Suit. Doesn't factor Rank within the Suit when sorting
public void sortyBySuit()
cards.sort(Comparator.comparing(Card::getSuit));


// sorts cards in the Hand by Rank. Doesn't factor Suit when sorting.
public void sortByRank()
cards.sort(Comparator.comparing(Card::getRank));


// sorts cards in the Hand by Color. Doesn't factor Suit or Rank when sorting.
public void sortByColor()
cards.sort(Comparator.comparing(Card::getColor));


// sorts cards in the Hand by Suit then Rank in ascending order.
public void sort()
cards.sort((Card first, Card second) -> first.compareTo(second));


// allows a card in the hand to be flipped in the opposite orientation
public void flipCard(int index)
cards.get(index).flip();




PlayingCard.java



package com.frijolie.cards;

import java.util.Objects;

public class PlayingCard implements Card

private final Rank rank;
private final Suit suit;
private boolean isFaceUp;

// default constructor
public PlayingCard(final Rank rank, final Suit suit)
this.rank = rank;
this.suit = suit;
isFaceUp = true;


... // snipping implemented accessor methods from Card interface

// returns a string representation of the card
@Override
public String toString()
return rank.getLetter() + suit.getSymbol();


// compares one PlayingCard to another
@Override
public boolean equals(final Object o)
Objects.requireNonNull(o);
boolean isEqual;

if (!(o instanceof Card))
isEqual = false;
else if (this == 0)
isEqual = true;
else
var card = (Card) o;
isEqual = (this.getRank().equals(card.getRank()) && this.suit.equals(card.getSuit()));

return isEqual;


// generates unique int value of a card
@Override
public int hashCode()
int suitMultiplier = suit.ordinal() + 2;
int rankMultiplier = rank.ordinal() + 3;
return (suitMultiplier * rankMultiplier) * 31;


// flip the card in the opposite orientation, faceUp/faceDown
@Override
public void flip()
isFaceUp = !isFaceUp;


// returns true if the card is faceUp
@Override
public boolean isFaceUp()
return isFaceUp;


// compares one card to another and returns an integer value
@Override
public int compareTo(Card o)
var card = Objects.requireNonNull(o);
var suitCompareResult = this.suit.compareTo(card.getSuit());
if (suitCompareResult != 0)
return suitCompareResult;
else
return this.rank.compareTo(card.getRank());





What could I improve?



I've toyed with the idea to make Hand and/or Deck an interface. However, I opted to go with classes in favor of code reuse and contained behavior. I send unmodifiable collections (Hand, Deck) as I want to control the behavior of the internal collection within their respective classes. You can see what it looks like, but you can't modify the Deck or Hand outside the class. Is this a good defensive strategy?



I'm also not 100% comfortable with the requirement of a Rank and Suit. How about a Magic the Gathering Card? That doesn't have a suit, but you could argue that it has a rank. Is it better to leave the implementation up to the other developer? Same could be said for an Uno card, Color.Red and Color.BLACK aren't the only options there.



I've thought about the Rank enum also. If the game you model has an ACE as a high card (not low as supplied here), how would you fix that in your own implementation? Create a new AceHighRank and declare them in a different order? However, that would break PlayingCard because it's expecting a Rank (not AceHighRank). Right?







share|improve this question













I'm looking for an honest review of what I've got thus far. I'm still trying to get my head wrapped around software development and have picked Java as my starting language. That said, I figured that I would attempt to create a reusable module for which I'd use this as an external library.



I think it would be invaluable to hear from you experts on where I can improve or refactor. The code here is just snippets. Full code on GitHub.



Card.java



package com.frijolie.cards;

public interface Card extends Comparable<Card>

// returns the value of the Card. inherently coupled with the Rank
int getValue();

// returns the Rank of the Card. Values from Ace,2,3..., Jack,Queen,King
Rank getRank();

// returns the Suit of the Card. Values are Diamonds, Hearts, Spades, Clubs
Suit getSuit();

// returns the color of the Card. Values are Red or Black
Color getColor();

// display the card onscreen
void displayCard();

// compares this Card to another, returns if they are equal
boolean equals(final Object o);

// Generates a unique int value of a Card
int hashCode();

// flips a Card faceUp or faceDown
void flip();

// returns true if this Card is faceUp
boolean isFaceUp();




Rank.java



package com.frijlie.cards;

public enum Rank

ACE("Ace","A",1),
TWO("Two","2",2),
THREE("Three","3",3),
... // snipped for brevity
JACK("Jack","J",10),
QUEEN("Queen","Q",10),
KING("King","K","10);

private final String name;
private final String letter;
private final int value;

Rank(final String name, final String letter, final int value)
this.name = name;
this.letter = letter;
this.value = value;


... // accessor methods




Suit.java



package com.frijolie.cards;

public enum Suit
CLUBS("Clubs",'u2663', Color.BLACK),
DIAMONDS("Diamonds",'u2666', Color.RED),
SPADES("Spades",'u2660', Color.BLACK),
HEARTS("Hearts",'u2764', Color.RED);

private final String name;
private final char symbol;
private final Color color;

Suit(final String name, final char symbol, final Color color)
this.name = name;
this.symbol = symbol;
this.color = color;


... // accessor methods



Color.java



package com.frijolie.cards;

public enum Color
RED("Red"),
BLACK("Black");

private final String name;

Color(final String name)
this.name = name;


public final String getName()
return name;




Deck.java



package com.frijolie;

// snipped imports

public class Deck

private boolean isShuffled;
private Stack<Card> deck;

// default constructor, will create a single deck of 52 cards
public Deck()
this(1)


// overloaded constructor, will create multiple decks of 52 cards
public deck(int numberOfDecks)
deck = new Stack<>();
clearDeck();
for (int i = 0; i < numberOfDecks; i++)
populateDeck();



// a method to shuffle the collection of cards. default will shuffle 1x
public void shuffle()
shuffle(1);


// overloaded shuffle method. will shuffle the deck a number of times.
public void shuffle(int numberOfTimes)
isShuffled = true;
for (int i = 0; i < numberOfTimes; i++)
Collections.shuffle(deck);



// returns true if the deck has been shuffled
public boolean isShuffled()
return isShuffled;


// returns the number of cards remaining in the deck
public int numberOfCards()
return deck.size();


// a method to remove a card from the deck
public Card removeCard(Card card)
if(deck.contains(Objects.requireNonNull(card)))
int index = deck.indexOf(card);
return deck.remove(index);
else
return null;



// returns true if the specified card is contained in the deck
public boolean containsCard(Card card)
return deck.contains(Objects.requireNonNull(card));


// populates the deck with all possible cards in the deck. 52 cards
public void populateDeck()
for(Suit suit: Suit.values())
for(Rank rank: Rank.values())
deck.add(new PlayingCard(rank,suit));




// Overloaded method which allows any number of decks of 52 cards.
public void populateDeck(int numberOfDecks)
for(int i = 0; i < numberOfDecks; i++)
populateDeck();



// remove all cards from the deck
public void clearDeck()
deck.clear();


// returns an unmodifiable collection, a view, of the deck.
public Collection<Card> getUnmodifiableCollection()
return new ArrayList<>(deck);


// returns the first card in the collection.
public Card pop()
return deck.pop();




Hand.java



package com.frijolie.cards;

// snipped imports

public class Hand implements Comparable<Hand>

private List<Card> cards;

// default constructor
public Hand()
cards = new ArrayList<>();


// will add a specific card to the Hand
public void addCard(Card card)
cards.add(Objects.requireNonNull(card));


// will remove a specific card from the Hand if it's contained in the Hand
public Card removeCard(Card card)
if (cards.contains(Objects.requireNonNull(card)))
int index = cards.indexOf(card);
return cards.remove(index);
else
throw new IllegalArgumentException("This card did not exist in the hand. It cannot be removed.");



// method to display all the Cards in the Hand
public void displayHand()
for (Card card : cards)
if (card.isFaceUp())
card.displayCard();
else
System.out.println("[ Card Back ]");




// returns the sum of the value of all Cards in the Hand
public int calculateValue()
var handValue = 0;
for (Card card : cards)
handValue += card.getRank().getValue();

return handValue;


// remove all cards in the Hand
public void clearHand()
cards.clear();


// returns a unmodifiable collection, a view, of all cards in the Hand
public Collection<Card> getUnmodifiableCollection()
return Collections.unmodifiableCollection(cards);


// compares one Hand to another
public int compareTo(final Hand otherHand)
return this.calculateValue() - otherHand.calculateValue();


// sorts cards in the Hand by Suit. Doesn't factor Rank within the Suit when sorting
public void sortyBySuit()
cards.sort(Comparator.comparing(Card::getSuit));


// sorts cards in the Hand by Rank. Doesn't factor Suit when sorting.
public void sortByRank()
cards.sort(Comparator.comparing(Card::getRank));


// sorts cards in the Hand by Color. Doesn't factor Suit or Rank when sorting.
public void sortByColor()
cards.sort(Comparator.comparing(Card::getColor));


// sorts cards in the Hand by Suit then Rank in ascending order.
public void sort()
cards.sort((Card first, Card second) -> first.compareTo(second));


// allows a card in the hand to be flipped in the opposite orientation
public void flipCard(int index)
cards.get(index).flip();




PlayingCard.java



package com.frijolie.cards;

import java.util.Objects;

public class PlayingCard implements Card

private final Rank rank;
private final Suit suit;
private boolean isFaceUp;

// default constructor
public PlayingCard(final Rank rank, final Suit suit)
this.rank = rank;
this.suit = suit;
isFaceUp = true;


... // snipping implemented accessor methods from Card interface

// returns a string representation of the card
@Override
public String toString()
return rank.getLetter() + suit.getSymbol();


// compares one PlayingCard to another
@Override
public boolean equals(final Object o)
Objects.requireNonNull(o);
boolean isEqual;

if (!(o instanceof Card))
isEqual = false;
else if (this == 0)
isEqual = true;
else
var card = (Card) o;
isEqual = (this.getRank().equals(card.getRank()) && this.suit.equals(card.getSuit()));

return isEqual;


// generates unique int value of a card
@Override
public int hashCode()
int suitMultiplier = suit.ordinal() + 2;
int rankMultiplier = rank.ordinal() + 3;
return (suitMultiplier * rankMultiplier) * 31;


// flip the card in the opposite orientation, faceUp/faceDown
@Override
public void flip()
isFaceUp = !isFaceUp;


// returns true if the card is faceUp
@Override
public boolean isFaceUp()
return isFaceUp;


// compares one card to another and returns an integer value
@Override
public int compareTo(Card o)
var card = Objects.requireNonNull(o);
var suitCompareResult = this.suit.compareTo(card.getSuit());
if (suitCompareResult != 0)
return suitCompareResult;
else
return this.rank.compareTo(card.getRank());





What could I improve?



I've toyed with the idea to make Hand and/or Deck an interface. However, I opted to go with classes in favor of code reuse and contained behavior. I send unmodifiable collections (Hand, Deck) as I want to control the behavior of the internal collection within their respective classes. You can see what it looks like, but you can't modify the Deck or Hand outside the class. Is this a good defensive strategy?



I'm also not 100% comfortable with the requirement of a Rank and Suit. How about a Magic the Gathering Card? That doesn't have a suit, but you could argue that it has a rank. Is it better to leave the implementation up to the other developer? Same could be said for an Uno card, Color.Red and Color.BLACK aren't the only options there.



I've thought about the Rank enum also. If the game you model has an ACE as a high card (not low as supplied here), how would you fix that in your own implementation? Create a new AceHighRank and declare them in a different order? However, that would break PlayingCard because it's expecting a Rank (not AceHighRank). Right?









share|improve this question












share|improve this question




share|improve this question








edited May 11 at 1:31









Jamal♦

30.1k11114225




30.1k11114225









asked May 10 at 18:38









Frijolie

313




313







  • 3




    "Go easy on me" … "I look forward to your honest review. I have thick skin. I can take it." — Which do you want?
    – 200_success
    May 10 at 19:08










  • Touché. Updated.
    – Frijolie
    May 10 at 19:12










  • Nothing wrong with being self-taught.
    – The Mattbat999
    May 10 at 19:26






  • 2




    Welcome to Code Review! Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
    – Malachi♦
    May 10 at 19:45












  • 3




    "Go easy on me" … "I look forward to your honest review. I have thick skin. I can take it." — Which do you want?
    – 200_success
    May 10 at 19:08










  • Touché. Updated.
    – Frijolie
    May 10 at 19:12










  • Nothing wrong with being self-taught.
    – The Mattbat999
    May 10 at 19:26






  • 2




    Welcome to Code Review! Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
    – Malachi♦
    May 10 at 19:45







3




3




"Go easy on me" … "I look forward to your honest review. I have thick skin. I can take it." — Which do you want?
– 200_success
May 10 at 19:08




"Go easy on me" … "I look forward to your honest review. I have thick skin. I can take it." — Which do you want?
– 200_success
May 10 at 19:08












Touché. Updated.
– Frijolie
May 10 at 19:12




Touché. Updated.
– Frijolie
May 10 at 19:12












Nothing wrong with being self-taught.
– The Mattbat999
May 10 at 19:26




Nothing wrong with being self-taught.
– The Mattbat999
May 10 at 19:26




2




2




Welcome to Code Review! Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
– Malachi♦
May 10 at 19:45




Welcome to Code Review! Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
– Malachi♦
May 10 at 19:45










3 Answers
3






active

oldest

votes

















up vote
2
down vote













I think you are really thinking well about what is going on, what potential up and downsides are. I am not really familiar with card games, but my initial guess would be that it may be a bit long stretched to find a unified interface for all types of card games. In general you have cards, hands and decks, but the similarities can pretty much end there.



I also like that you used good descriptive names, private fields, and final where possible (though you are not entirely consistent with it). There is quite a bit of code, so I'll comment on the major code wise things that spring to mind:



  • The method comments should really be changed to true JavaDoc, in order for them to be useful to the users of your library. EDIT: apparently you already did this in your GitHub version.


  • I am not sure if you would really want to force implementors to implement equals/hashcode.


  • Like you said, the Rank (more specifically, it's value) can differ from game to game. This would make more sense in the Card implementation itself I'd think. Possibly even higher up?


  • isShuffled in Deck seems a bit strange to me. Why would you need it? As soon as you shuffle the deck, it will also always be true.


  • You should generally not use the Stack class in Java, see Stack


  • You do not need to loop yourself in order to fill the deck multiple times, you already have a method for that!


  • I would throw an exception when someone passes a card which is not contained in the deck, instead of null.


  • Think about how you would support decks for games which would not use the entire range of cards.


  • You are returning a Collection, which does not imply order. Your deck is very much ordered, so return a List, or your stack-like class. Also, the method does not tell me anything about what it is giving me.


  • I would rename pop to something like drawCard.


  • I would not mix user interface code within your domain objects, like e.g. the displayHand method. What if you would want to use it with a realistic 3D user interface?


  • The compareTo method in Hand is implemented somewhat risky. Only because you know value is always positive, it always works. Just something to think about.


  • Nice use of method references with the Comparator! You could use a static import to write sort(comparing(Card::getSuit)). You can also use this for the basic sort method: cards.sort(Card::compareTo).


  • Return early. In the equals method, as soon as you know it is not equal, return false. Don't introduce unnecessary variables, unless they would help with readability.


  • Your hashCode implementation is not really good, e.g. the hashcode of suit.ordinal = 1, rank.ordinal = 2 is equal to the one of suit.ordinal = 3, rank.ordinal = 0, and there are more to be found. You can look up how to implement a correct hashcode, or use some auto generation in your IDE to get started. Returning a lot of equal values is not wrong, but can slow down e.g. hashing data structures.


Your question about only changing state through the public interface: definitely yes! It is a really good principle. It makes sure you are in control.



Finally, this is me, but I am not much of a fan of using the get prefix. Apart from implying it always is a simple field acces (which granted, in this case is generally true), for me it feels superfluous. Apparently, it also has a name, namely the Uniform Access Principle. There are some interesting reads about this I think.






share|improve this answer























  • Wow! Thank you for such a comprehensive review. I should have indicated I omitted several details in an interest of brevity (i.e. true Javadocs). The intent of isShuffled() was to indicate to the clients if the deck has ever been shuffled. I didn't assume a deck would always be shuffled. Therefore, thought a flag would be necessary. Why no Stack? I've also heard don't use LinkedList or Queues either. A Deck is a LIFO collection. Thought it was a good fit. Every card should have a way to display itself, figured leave that up to the client (console or GUI). I'll look into UAP.
    – Frijolie
    May 11 at 17:30










  • It's not really a stack in itself that is the problem, but the Java implementation leaves something to be desired, see e.g. stackoverflow.com/questions/12524826/… . As you state yourself, it is a LIFO data structure, however you are also indexing it when removing elements, something it is not made for (the fact that you can is an example of the questionable design). There is nothing wrong with LinkedList or Queue, it just depends on the use case. You can take a look at the how each one behaves, see e.g. bigocheatsheet.com
    – Koekje
    May 11 at 17:52

















up vote
2
down vote













Every competent non-junior developer is self-taught, because you don't learn everything you need to know in school.



This is actually a reasonably tricky problem you've picked out, so don't be discouraged by all the feedback. You need to spend more time thinking about the bounds between application-specific code (Poker rules, Uno rules, etc) and the generic library code (How does a Deck work, what does a Card look like). Right now it's all mixed together. That's not surprising - building good APIs is hard.



It also seems like you have a lot of methods that serve no particular purpose because you think they might be handy later. It's really easy to add and really hard to remove, and YAGNI. Wait for a use case before adding things, and make sure you do it right, because if you have to fix it all your clients will break.



Card



  • Use javadoc, not comments


  • equals and hashCode aren't doing anything. All objects have them, so every Card will always fulfill that part of the contract without doing any extra work.


  • Consider having flip() return the new state of the card.


  • What is the point of this interface? If you're only building for poker cards, the interface is noise. If you're building for other kinds of cards, you're forcing a ton of false requirements - Uno cards, for instance, can't fulfill this contract. Strongly consider making this a marker interface if you want to support non-poker games.


  • The methods are mixed-up. On the one hand, clients can ask your for your internals and you freely share them. On the other hand, the card renders itself with no inputs. Either keep your privates private and use instead methods like hasRank(Rank rank) or get rid of the render method and let the client do it.


Color



  • Having a class with the same name as a class in the Java API is a bad idea. You're asking for conflicts. It's not really a Color, it's a CardColor or some such.

Deck



  • Deck needs to be final. You can't have users extending Deck with their own functionality (like putting all the good cards in one place when "shuffling")s


  • A deck is a well-understood concept. It's nonsensical to create a deck and specify the number of decks it has. You need a separate concept, a Shoe. Shoes can hold multiple decks of poker cards in them.


  • You don't need to clear a new Deck. It's already clear.


  • This isn't a generic Deck class. It's a poker deck. Do you want something useful for other games? If so, you need to take all the poker functionality into a separate class.


  • Shuffling more than once in a row is not sensical in code. The deck will be no more shuffled after the eighth time than the first.


  • isShuffled() is not useful. After you call shuffle, the deck is shuffled. Unless you add some way to unshuffle a deck?


  • I doubt the utility of removeCard(Card) and hasCard(Card). I guess there might be games where they are helpful, but I can't think of any.


  • It's visually ugly to use the return value of Objects.requireNonNull() like that. Declare it as a standalone call.


  • populateDeck() again assumes a poker deck. And it's public, which means any yahoo with access to the deck can throw in cards at any time.


  • clearDeck() is the same problem. And what does it mean for a deck to be cleared? In real life, you don't ever get rid of all the cards, still have a "deck", and then add cards back in. You might take cards out of the deck or put them back in, but that's different than the abstraction you have.


  • What's the use case for giving out a list of the cards? You should return a List, not a Collection, because decks typically have an order.


  • A deck is usually not a stack. A Deque might be more appropriate.


  • pop is leaking internal details. Nobody says "pop me a card off that deck".


Hand



  • Needs to be final for the same reason as Deck.


  • Is there a use case for removing a specific card from a Hand?


  • Again, keep the display code out of the model. Either pass in something to do the rendering or have the UI call methods on Hand to render.


  • calculateValue - is there a use case for this? What game uses as a value the sum of all the cards?


  • clearHand - this needs a better name, because that's not what people talk about doing in real life. Also, it should return the cards you just tossed, to facilitate putting them back in the deck. Or maybe pass in a deck and add the cards back to it?


  • compareTo - there are lots of different ways to compare the strengths of two hands. You shouldn't be picking one and hard-coding it here.


  • Don't have multiple hard-coded sort methods. Have one method that takes a comparator. Otherwise every time any client wants a new sort method, the API needs to change.


  • Maybe revealed would be better than flipped? I also think that the revealed state of a card belongs here, not on the Card.


Playing Card



  • Does isFaceUp make sense on PlayingCard? I guess it might if your deck can have face-up cards in it. But strongly consider if that should be external state. That sounds like it belongs to the UI layer, not the model.


  • Return early from equals(). The reason for a single return was made obsolete years ago.


  • Strongly consider whether you want to implement compareTo(). Some games have trump suits which can change the natural ordering of the cards in the deck.


I took a stab at a generic deck class, and came up with this. It should probably have a draw(int cards) and overloaded discard methods also, but I need to pack.



//Note that this class is *NOT* thread safe. It will definitely have problems in a multi-threaded environment.
public final class Deck<T extends Card>

private final List<T> deck = new LinkedList<>();
private final Set<T> outstandingCards = new HashSet<>();
private final List<T> discardPile = new LinkedList<>();

public Deck(final Collection<T> cards)
super();
this.deck.addAll(cards);


public void shuffle()
this.deck.addAll(this.discardPile);
this.discardPile.clear();
Collections.shuffle(this.deck);


public boolean isDeckEmpty()
return this.deck.isEmpty();


public int deckSize()
return this.deck.size();


public T draw()
if (this.isDeckEmpty())
throw new IllegalStateException();


final T card = this.deck.remove(0);
this.outstandingCards.add(card);
return card;


public void discard(final T card)
if (!this.outstandingCards.contains(card))
// Somebody tried to sneak a new card into the deck!
throw new IllegalStateException();

this.outstandingCards.remove(card);
this.discardPile.add(card);







share|improve this answer





















  • Good points! One minor thing I'd like to note is that shuffling multiple times can actually have an effect I think. The basic Collections#shuffle seems to be using Java's default Random, which if I am not mistaken uses a 48-bit seed, way less than 52!. So shuffling multiple times would allow you to perhaps reach more states? That does not mean it is required of course, and if it is, it probably needs to be implemented differently.
    – Koekje
    May 11 at 11:02











  • Thank you for your review. I'm still processing your comments. Mind blown. You're exactly right, I do have UI code mixed with business rules. I need to separate. You're also spot on with the YAGNI comment. Many methods were added to anticipate a need. What's meant by a card state? CardState.FACE_UP? I get the concept of a shoe. However, why is clear() and (re)populateDeck() not a good strategy? Worth the overhead to discard and add again? Hand#calculateValue is for Blackjack--which is my intended purpose. Great idea! I'll refactor to sort(Comparator c)
    – Frijolie
    May 12 at 16:00

















up vote
1
down vote













In Deck.java you have:



if(deck.contains(Objects.requireNonNull(card))) {


but you have a function containsCard that could shorten it:



if(containsCard(card))





share|improve this answer























    Your Answer




    StackExchange.ifUsing("editor", function ()
    return StackExchange.using("mathjaxEditing", function ()
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    );
    );
    , "mathjax-editing");

    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "196"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );








     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f194134%2fmy-first-deck-of-cards%23new-answer', 'question_page');

    );

    Post as a guest






























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    2
    down vote













    I think you are really thinking well about what is going on, what potential up and downsides are. I am not really familiar with card games, but my initial guess would be that it may be a bit long stretched to find a unified interface for all types of card games. In general you have cards, hands and decks, but the similarities can pretty much end there.



    I also like that you used good descriptive names, private fields, and final where possible (though you are not entirely consistent with it). There is quite a bit of code, so I'll comment on the major code wise things that spring to mind:



    • The method comments should really be changed to true JavaDoc, in order for them to be useful to the users of your library. EDIT: apparently you already did this in your GitHub version.


    • I am not sure if you would really want to force implementors to implement equals/hashcode.


    • Like you said, the Rank (more specifically, it's value) can differ from game to game. This would make more sense in the Card implementation itself I'd think. Possibly even higher up?


    • isShuffled in Deck seems a bit strange to me. Why would you need it? As soon as you shuffle the deck, it will also always be true.


    • You should generally not use the Stack class in Java, see Stack


    • You do not need to loop yourself in order to fill the deck multiple times, you already have a method for that!


    • I would throw an exception when someone passes a card which is not contained in the deck, instead of null.


    • Think about how you would support decks for games which would not use the entire range of cards.


    • You are returning a Collection, which does not imply order. Your deck is very much ordered, so return a List, or your stack-like class. Also, the method does not tell me anything about what it is giving me.


    • I would rename pop to something like drawCard.


    • I would not mix user interface code within your domain objects, like e.g. the displayHand method. What if you would want to use it with a realistic 3D user interface?


    • The compareTo method in Hand is implemented somewhat risky. Only because you know value is always positive, it always works. Just something to think about.


    • Nice use of method references with the Comparator! You could use a static import to write sort(comparing(Card::getSuit)). You can also use this for the basic sort method: cards.sort(Card::compareTo).


    • Return early. In the equals method, as soon as you know it is not equal, return false. Don't introduce unnecessary variables, unless they would help with readability.


    • Your hashCode implementation is not really good, e.g. the hashcode of suit.ordinal = 1, rank.ordinal = 2 is equal to the one of suit.ordinal = 3, rank.ordinal = 0, and there are more to be found. You can look up how to implement a correct hashcode, or use some auto generation in your IDE to get started. Returning a lot of equal values is not wrong, but can slow down e.g. hashing data structures.


    Your question about only changing state through the public interface: definitely yes! It is a really good principle. It makes sure you are in control.



    Finally, this is me, but I am not much of a fan of using the get prefix. Apart from implying it always is a simple field acces (which granted, in this case is generally true), for me it feels superfluous. Apparently, it also has a name, namely the Uniform Access Principle. There are some interesting reads about this I think.






    share|improve this answer























    • Wow! Thank you for such a comprehensive review. I should have indicated I omitted several details in an interest of brevity (i.e. true Javadocs). The intent of isShuffled() was to indicate to the clients if the deck has ever been shuffled. I didn't assume a deck would always be shuffled. Therefore, thought a flag would be necessary. Why no Stack? I've also heard don't use LinkedList or Queues either. A Deck is a LIFO collection. Thought it was a good fit. Every card should have a way to display itself, figured leave that up to the client (console or GUI). I'll look into UAP.
      – Frijolie
      May 11 at 17:30










    • It's not really a stack in itself that is the problem, but the Java implementation leaves something to be desired, see e.g. stackoverflow.com/questions/12524826/… . As you state yourself, it is a LIFO data structure, however you are also indexing it when removing elements, something it is not made for (the fact that you can is an example of the questionable design). There is nothing wrong with LinkedList or Queue, it just depends on the use case. You can take a look at the how each one behaves, see e.g. bigocheatsheet.com
      – Koekje
      May 11 at 17:52














    up vote
    2
    down vote













    I think you are really thinking well about what is going on, what potential up and downsides are. I am not really familiar with card games, but my initial guess would be that it may be a bit long stretched to find a unified interface for all types of card games. In general you have cards, hands and decks, but the similarities can pretty much end there.



    I also like that you used good descriptive names, private fields, and final where possible (though you are not entirely consistent with it). There is quite a bit of code, so I'll comment on the major code wise things that spring to mind:



    • The method comments should really be changed to true JavaDoc, in order for them to be useful to the users of your library. EDIT: apparently you already did this in your GitHub version.


    • I am not sure if you would really want to force implementors to implement equals/hashcode.


    • Like you said, the Rank (more specifically, it's value) can differ from game to game. This would make more sense in the Card implementation itself I'd think. Possibly even higher up?


    • isShuffled in Deck seems a bit strange to me. Why would you need it? As soon as you shuffle the deck, it will also always be true.


    • You should generally not use the Stack class in Java, see Stack


    • You do not need to loop yourself in order to fill the deck multiple times, you already have a method for that!


    • I would throw an exception when someone passes a card which is not contained in the deck, instead of null.


    • Think about how you would support decks for games which would not use the entire range of cards.


    • You are returning a Collection, which does not imply order. Your deck is very much ordered, so return a List, or your stack-like class. Also, the method does not tell me anything about what it is giving me.


    • I would rename pop to something like drawCard.


    • I would not mix user interface code within your domain objects, like e.g. the displayHand method. What if you would want to use it with a realistic 3D user interface?


    • The compareTo method in Hand is implemented somewhat risky. Only because you know value is always positive, it always works. Just something to think about.


    • Nice use of method references with the Comparator! You could use a static import to write sort(comparing(Card::getSuit)). You can also use this for the basic sort method: cards.sort(Card::compareTo).


    • Return early. In the equals method, as soon as you know it is not equal, return false. Don't introduce unnecessary variables, unless they would help with readability.


    • Your hashCode implementation is not really good, e.g. the hashcode of suit.ordinal = 1, rank.ordinal = 2 is equal to the one of suit.ordinal = 3, rank.ordinal = 0, and there are more to be found. You can look up how to implement a correct hashcode, or use some auto generation in your IDE to get started. Returning a lot of equal values is not wrong, but can slow down e.g. hashing data structures.


    Your question about only changing state through the public interface: definitely yes! It is a really good principle. It makes sure you are in control.



    Finally, this is me, but I am not much of a fan of using the get prefix. Apart from implying it always is a simple field acces (which granted, in this case is generally true), for me it feels superfluous. Apparently, it also has a name, namely the Uniform Access Principle. There are some interesting reads about this I think.






    share|improve this answer























    • Wow! Thank you for such a comprehensive review. I should have indicated I omitted several details in an interest of brevity (i.e. true Javadocs). The intent of isShuffled() was to indicate to the clients if the deck has ever been shuffled. I didn't assume a deck would always be shuffled. Therefore, thought a flag would be necessary. Why no Stack? I've also heard don't use LinkedList or Queues either. A Deck is a LIFO collection. Thought it was a good fit. Every card should have a way to display itself, figured leave that up to the client (console or GUI). I'll look into UAP.
      – Frijolie
      May 11 at 17:30










    • It's not really a stack in itself that is the problem, but the Java implementation leaves something to be desired, see e.g. stackoverflow.com/questions/12524826/… . As you state yourself, it is a LIFO data structure, however you are also indexing it when removing elements, something it is not made for (the fact that you can is an example of the questionable design). There is nothing wrong with LinkedList or Queue, it just depends on the use case. You can take a look at the how each one behaves, see e.g. bigocheatsheet.com
      – Koekje
      May 11 at 17:52












    up vote
    2
    down vote










    up vote
    2
    down vote









    I think you are really thinking well about what is going on, what potential up and downsides are. I am not really familiar with card games, but my initial guess would be that it may be a bit long stretched to find a unified interface for all types of card games. In general you have cards, hands and decks, but the similarities can pretty much end there.



    I also like that you used good descriptive names, private fields, and final where possible (though you are not entirely consistent with it). There is quite a bit of code, so I'll comment on the major code wise things that spring to mind:



    • The method comments should really be changed to true JavaDoc, in order for them to be useful to the users of your library. EDIT: apparently you already did this in your GitHub version.


    • I am not sure if you would really want to force implementors to implement equals/hashcode.


    • Like you said, the Rank (more specifically, it's value) can differ from game to game. This would make more sense in the Card implementation itself I'd think. Possibly even higher up?


    • isShuffled in Deck seems a bit strange to me. Why would you need it? As soon as you shuffle the deck, it will also always be true.


    • You should generally not use the Stack class in Java, see Stack


    • You do not need to loop yourself in order to fill the deck multiple times, you already have a method for that!


    • I would throw an exception when someone passes a card which is not contained in the deck, instead of null.


    • Think about how you would support decks for games which would not use the entire range of cards.


    • You are returning a Collection, which does not imply order. Your deck is very much ordered, so return a List, or your stack-like class. Also, the method does not tell me anything about what it is giving me.


    • I would rename pop to something like drawCard.


    • I would not mix user interface code within your domain objects, like e.g. the displayHand method. What if you would want to use it with a realistic 3D user interface?


    • The compareTo method in Hand is implemented somewhat risky. Only because you know value is always positive, it always works. Just something to think about.


    • Nice use of method references with the Comparator! You could use a static import to write sort(comparing(Card::getSuit)). You can also use this for the basic sort method: cards.sort(Card::compareTo).


    • Return early. In the equals method, as soon as you know it is not equal, return false. Don't introduce unnecessary variables, unless they would help with readability.


    • Your hashCode implementation is not really good, e.g. the hashcode of suit.ordinal = 1, rank.ordinal = 2 is equal to the one of suit.ordinal = 3, rank.ordinal = 0, and there are more to be found. You can look up how to implement a correct hashcode, or use some auto generation in your IDE to get started. Returning a lot of equal values is not wrong, but can slow down e.g. hashing data structures.


    Your question about only changing state through the public interface: definitely yes! It is a really good principle. It makes sure you are in control.



    Finally, this is me, but I am not much of a fan of using the get prefix. Apart from implying it always is a simple field acces (which granted, in this case is generally true), for me it feels superfluous. Apparently, it also has a name, namely the Uniform Access Principle. There are some interesting reads about this I think.






    share|improve this answer















    I think you are really thinking well about what is going on, what potential up and downsides are. I am not really familiar with card games, but my initial guess would be that it may be a bit long stretched to find a unified interface for all types of card games. In general you have cards, hands and decks, but the similarities can pretty much end there.



    I also like that you used good descriptive names, private fields, and final where possible (though you are not entirely consistent with it). There is quite a bit of code, so I'll comment on the major code wise things that spring to mind:



    • The method comments should really be changed to true JavaDoc, in order for them to be useful to the users of your library. EDIT: apparently you already did this in your GitHub version.


    • I am not sure if you would really want to force implementors to implement equals/hashcode.


    • Like you said, the Rank (more specifically, it's value) can differ from game to game. This would make more sense in the Card implementation itself I'd think. Possibly even higher up?


    • isShuffled in Deck seems a bit strange to me. Why would you need it? As soon as you shuffle the deck, it will also always be true.


    • You should generally not use the Stack class in Java, see Stack


    • You do not need to loop yourself in order to fill the deck multiple times, you already have a method for that!


    • I would throw an exception when someone passes a card which is not contained in the deck, instead of null.


    • Think about how you would support decks for games which would not use the entire range of cards.


    • You are returning a Collection, which does not imply order. Your deck is very much ordered, so return a List, or your stack-like class. Also, the method does not tell me anything about what it is giving me.


    • I would rename pop to something like drawCard.


    • I would not mix user interface code within your domain objects, like e.g. the displayHand method. What if you would want to use it with a realistic 3D user interface?


    • The compareTo method in Hand is implemented somewhat risky. Only because you know value is always positive, it always works. Just something to think about.


    • Nice use of method references with the Comparator! You could use a static import to write sort(comparing(Card::getSuit)). You can also use this for the basic sort method: cards.sort(Card::compareTo).


    • Return early. In the equals method, as soon as you know it is not equal, return false. Don't introduce unnecessary variables, unless they would help with readability.


    • Your hashCode implementation is not really good, e.g. the hashcode of suit.ordinal = 1, rank.ordinal = 2 is equal to the one of suit.ordinal = 3, rank.ordinal = 0, and there are more to be found. You can look up how to implement a correct hashcode, or use some auto generation in your IDE to get started. Returning a lot of equal values is not wrong, but can slow down e.g. hashing data structures.


    Your question about only changing state through the public interface: definitely yes! It is a really good principle. It makes sure you are in control.



    Finally, this is me, but I am not much of a fan of using the get prefix. Apart from implying it always is a simple field acces (which granted, in this case is generally true), for me it feels superfluous. Apparently, it also has a name, namely the Uniform Access Principle. There are some interesting reads about this I think.







    share|improve this answer















    share|improve this answer



    share|improve this answer








    edited May 10 at 20:14


























    answered May 10 at 20:01









    Koekje

    1,017211




    1,017211











    • Wow! Thank you for such a comprehensive review. I should have indicated I omitted several details in an interest of brevity (i.e. true Javadocs). The intent of isShuffled() was to indicate to the clients if the deck has ever been shuffled. I didn't assume a deck would always be shuffled. Therefore, thought a flag would be necessary. Why no Stack? I've also heard don't use LinkedList or Queues either. A Deck is a LIFO collection. Thought it was a good fit. Every card should have a way to display itself, figured leave that up to the client (console or GUI). I'll look into UAP.
      – Frijolie
      May 11 at 17:30










    • It's not really a stack in itself that is the problem, but the Java implementation leaves something to be desired, see e.g. stackoverflow.com/questions/12524826/… . As you state yourself, it is a LIFO data structure, however you are also indexing it when removing elements, something it is not made for (the fact that you can is an example of the questionable design). There is nothing wrong with LinkedList or Queue, it just depends on the use case. You can take a look at the how each one behaves, see e.g. bigocheatsheet.com
      – Koekje
      May 11 at 17:52
















    • Wow! Thank you for such a comprehensive review. I should have indicated I omitted several details in an interest of brevity (i.e. true Javadocs). The intent of isShuffled() was to indicate to the clients if the deck has ever been shuffled. I didn't assume a deck would always be shuffled. Therefore, thought a flag would be necessary. Why no Stack? I've also heard don't use LinkedList or Queues either. A Deck is a LIFO collection. Thought it was a good fit. Every card should have a way to display itself, figured leave that up to the client (console or GUI). I'll look into UAP.
      – Frijolie
      May 11 at 17:30










    • It's not really a stack in itself that is the problem, but the Java implementation leaves something to be desired, see e.g. stackoverflow.com/questions/12524826/… . As you state yourself, it is a LIFO data structure, however you are also indexing it when removing elements, something it is not made for (the fact that you can is an example of the questionable design). There is nothing wrong with LinkedList or Queue, it just depends on the use case. You can take a look at the how each one behaves, see e.g. bigocheatsheet.com
      – Koekje
      May 11 at 17:52















    Wow! Thank you for such a comprehensive review. I should have indicated I omitted several details in an interest of brevity (i.e. true Javadocs). The intent of isShuffled() was to indicate to the clients if the deck has ever been shuffled. I didn't assume a deck would always be shuffled. Therefore, thought a flag would be necessary. Why no Stack? I've also heard don't use LinkedList or Queues either. A Deck is a LIFO collection. Thought it was a good fit. Every card should have a way to display itself, figured leave that up to the client (console or GUI). I'll look into UAP.
    – Frijolie
    May 11 at 17:30




    Wow! Thank you for such a comprehensive review. I should have indicated I omitted several details in an interest of brevity (i.e. true Javadocs). The intent of isShuffled() was to indicate to the clients if the deck has ever been shuffled. I didn't assume a deck would always be shuffled. Therefore, thought a flag would be necessary. Why no Stack? I've also heard don't use LinkedList or Queues either. A Deck is a LIFO collection. Thought it was a good fit. Every card should have a way to display itself, figured leave that up to the client (console or GUI). I'll look into UAP.
    – Frijolie
    May 11 at 17:30












    It's not really a stack in itself that is the problem, but the Java implementation leaves something to be desired, see e.g. stackoverflow.com/questions/12524826/… . As you state yourself, it is a LIFO data structure, however you are also indexing it when removing elements, something it is not made for (the fact that you can is an example of the questionable design). There is nothing wrong with LinkedList or Queue, it just depends on the use case. You can take a look at the how each one behaves, see e.g. bigocheatsheet.com
    – Koekje
    May 11 at 17:52




    It's not really a stack in itself that is the problem, but the Java implementation leaves something to be desired, see e.g. stackoverflow.com/questions/12524826/… . As you state yourself, it is a LIFO data structure, however you are also indexing it when removing elements, something it is not made for (the fact that you can is an example of the questionable design). There is nothing wrong with LinkedList or Queue, it just depends on the use case. You can take a look at the how each one behaves, see e.g. bigocheatsheet.com
    – Koekje
    May 11 at 17:52












    up vote
    2
    down vote













    Every competent non-junior developer is self-taught, because you don't learn everything you need to know in school.



    This is actually a reasonably tricky problem you've picked out, so don't be discouraged by all the feedback. You need to spend more time thinking about the bounds between application-specific code (Poker rules, Uno rules, etc) and the generic library code (How does a Deck work, what does a Card look like). Right now it's all mixed together. That's not surprising - building good APIs is hard.



    It also seems like you have a lot of methods that serve no particular purpose because you think they might be handy later. It's really easy to add and really hard to remove, and YAGNI. Wait for a use case before adding things, and make sure you do it right, because if you have to fix it all your clients will break.



    Card



    • Use javadoc, not comments


    • equals and hashCode aren't doing anything. All objects have them, so every Card will always fulfill that part of the contract without doing any extra work.


    • Consider having flip() return the new state of the card.


    • What is the point of this interface? If you're only building for poker cards, the interface is noise. If you're building for other kinds of cards, you're forcing a ton of false requirements - Uno cards, for instance, can't fulfill this contract. Strongly consider making this a marker interface if you want to support non-poker games.


    • The methods are mixed-up. On the one hand, clients can ask your for your internals and you freely share them. On the other hand, the card renders itself with no inputs. Either keep your privates private and use instead methods like hasRank(Rank rank) or get rid of the render method and let the client do it.


    Color



    • Having a class with the same name as a class in the Java API is a bad idea. You're asking for conflicts. It's not really a Color, it's a CardColor or some such.

    Deck



    • Deck needs to be final. You can't have users extending Deck with their own functionality (like putting all the good cards in one place when "shuffling")s


    • A deck is a well-understood concept. It's nonsensical to create a deck and specify the number of decks it has. You need a separate concept, a Shoe. Shoes can hold multiple decks of poker cards in them.


    • You don't need to clear a new Deck. It's already clear.


    • This isn't a generic Deck class. It's a poker deck. Do you want something useful for other games? If so, you need to take all the poker functionality into a separate class.


    • Shuffling more than once in a row is not sensical in code. The deck will be no more shuffled after the eighth time than the first.


    • isShuffled() is not useful. After you call shuffle, the deck is shuffled. Unless you add some way to unshuffle a deck?


    • I doubt the utility of removeCard(Card) and hasCard(Card). I guess there might be games where they are helpful, but I can't think of any.


    • It's visually ugly to use the return value of Objects.requireNonNull() like that. Declare it as a standalone call.


    • populateDeck() again assumes a poker deck. And it's public, which means any yahoo with access to the deck can throw in cards at any time.


    • clearDeck() is the same problem. And what does it mean for a deck to be cleared? In real life, you don't ever get rid of all the cards, still have a "deck", and then add cards back in. You might take cards out of the deck or put them back in, but that's different than the abstraction you have.


    • What's the use case for giving out a list of the cards? You should return a List, not a Collection, because decks typically have an order.


    • A deck is usually not a stack. A Deque might be more appropriate.


    • pop is leaking internal details. Nobody says "pop me a card off that deck".


    Hand



    • Needs to be final for the same reason as Deck.


    • Is there a use case for removing a specific card from a Hand?


    • Again, keep the display code out of the model. Either pass in something to do the rendering or have the UI call methods on Hand to render.


    • calculateValue - is there a use case for this? What game uses as a value the sum of all the cards?


    • clearHand - this needs a better name, because that's not what people talk about doing in real life. Also, it should return the cards you just tossed, to facilitate putting them back in the deck. Or maybe pass in a deck and add the cards back to it?


    • compareTo - there are lots of different ways to compare the strengths of two hands. You shouldn't be picking one and hard-coding it here.


    • Don't have multiple hard-coded sort methods. Have one method that takes a comparator. Otherwise every time any client wants a new sort method, the API needs to change.


    • Maybe revealed would be better than flipped? I also think that the revealed state of a card belongs here, not on the Card.


    Playing Card



    • Does isFaceUp make sense on PlayingCard? I guess it might if your deck can have face-up cards in it. But strongly consider if that should be external state. That sounds like it belongs to the UI layer, not the model.


    • Return early from equals(). The reason for a single return was made obsolete years ago.


    • Strongly consider whether you want to implement compareTo(). Some games have trump suits which can change the natural ordering of the cards in the deck.


    I took a stab at a generic deck class, and came up with this. It should probably have a draw(int cards) and overloaded discard methods also, but I need to pack.



    //Note that this class is *NOT* thread safe. It will definitely have problems in a multi-threaded environment.
    public final class Deck<T extends Card>

    private final List<T> deck = new LinkedList<>();
    private final Set<T> outstandingCards = new HashSet<>();
    private final List<T> discardPile = new LinkedList<>();

    public Deck(final Collection<T> cards)
    super();
    this.deck.addAll(cards);


    public void shuffle()
    this.deck.addAll(this.discardPile);
    this.discardPile.clear();
    Collections.shuffle(this.deck);


    public boolean isDeckEmpty()
    return this.deck.isEmpty();


    public int deckSize()
    return this.deck.size();


    public T draw()
    if (this.isDeckEmpty())
    throw new IllegalStateException();


    final T card = this.deck.remove(0);
    this.outstandingCards.add(card);
    return card;


    public void discard(final T card)
    if (!this.outstandingCards.contains(card))
    // Somebody tried to sneak a new card into the deck!
    throw new IllegalStateException();

    this.outstandingCards.remove(card);
    this.discardPile.add(card);







    share|improve this answer





















    • Good points! One minor thing I'd like to note is that shuffling multiple times can actually have an effect I think. The basic Collections#shuffle seems to be using Java's default Random, which if I am not mistaken uses a 48-bit seed, way less than 52!. So shuffling multiple times would allow you to perhaps reach more states? That does not mean it is required of course, and if it is, it probably needs to be implemented differently.
      – Koekje
      May 11 at 11:02











    • Thank you for your review. I'm still processing your comments. Mind blown. You're exactly right, I do have UI code mixed with business rules. I need to separate. You're also spot on with the YAGNI comment. Many methods were added to anticipate a need. What's meant by a card state? CardState.FACE_UP? I get the concept of a shoe. However, why is clear() and (re)populateDeck() not a good strategy? Worth the overhead to discard and add again? Hand#calculateValue is for Blackjack--which is my intended purpose. Great idea! I'll refactor to sort(Comparator c)
      – Frijolie
      May 12 at 16:00














    up vote
    2
    down vote













    Every competent non-junior developer is self-taught, because you don't learn everything you need to know in school.



    This is actually a reasonably tricky problem you've picked out, so don't be discouraged by all the feedback. You need to spend more time thinking about the bounds between application-specific code (Poker rules, Uno rules, etc) and the generic library code (How does a Deck work, what does a Card look like). Right now it's all mixed together. That's not surprising - building good APIs is hard.



    It also seems like you have a lot of methods that serve no particular purpose because you think they might be handy later. It's really easy to add and really hard to remove, and YAGNI. Wait for a use case before adding things, and make sure you do it right, because if you have to fix it all your clients will break.



    Card



    • Use javadoc, not comments


    • equals and hashCode aren't doing anything. All objects have them, so every Card will always fulfill that part of the contract without doing any extra work.


    • Consider having flip() return the new state of the card.


    • What is the point of this interface? If you're only building for poker cards, the interface is noise. If you're building for other kinds of cards, you're forcing a ton of false requirements - Uno cards, for instance, can't fulfill this contract. Strongly consider making this a marker interface if you want to support non-poker games.


    • The methods are mixed-up. On the one hand, clients can ask your for your internals and you freely share them. On the other hand, the card renders itself with no inputs. Either keep your privates private and use instead methods like hasRank(Rank rank) or get rid of the render method and let the client do it.


    Color



    • Having a class with the same name as a class in the Java API is a bad idea. You're asking for conflicts. It's not really a Color, it's a CardColor or some such.

    Deck



    • Deck needs to be final. You can't have users extending Deck with their own functionality (like putting all the good cards in one place when "shuffling")s


    • A deck is a well-understood concept. It's nonsensical to create a deck and specify the number of decks it has. You need a separate concept, a Shoe. Shoes can hold multiple decks of poker cards in them.


    • You don't need to clear a new Deck. It's already clear.


    • This isn't a generic Deck class. It's a poker deck. Do you want something useful for other games? If so, you need to take all the poker functionality into a separate class.


    • Shuffling more than once in a row is not sensical in code. The deck will be no more shuffled after the eighth time than the first.


    • isShuffled() is not useful. After you call shuffle, the deck is shuffled. Unless you add some way to unshuffle a deck?


    • I doubt the utility of removeCard(Card) and hasCard(Card). I guess there might be games where they are helpful, but I can't think of any.


    • It's visually ugly to use the return value of Objects.requireNonNull() like that. Declare it as a standalone call.


    • populateDeck() again assumes a poker deck. And it's public, which means any yahoo with access to the deck can throw in cards at any time.


    • clearDeck() is the same problem. And what does it mean for a deck to be cleared? In real life, you don't ever get rid of all the cards, still have a "deck", and then add cards back in. You might take cards out of the deck or put them back in, but that's different than the abstraction you have.


    • What's the use case for giving out a list of the cards? You should return a List, not a Collection, because decks typically have an order.


    • A deck is usually not a stack. A Deque might be more appropriate.


    • pop is leaking internal details. Nobody says "pop me a card off that deck".


    Hand



    • Needs to be final for the same reason as Deck.


    • Is there a use case for removing a specific card from a Hand?


    • Again, keep the display code out of the model. Either pass in something to do the rendering or have the UI call methods on Hand to render.


    • calculateValue - is there a use case for this? What game uses as a value the sum of all the cards?


    • clearHand - this needs a better name, because that's not what people talk about doing in real life. Also, it should return the cards you just tossed, to facilitate putting them back in the deck. Or maybe pass in a deck and add the cards back to it?


    • compareTo - there are lots of different ways to compare the strengths of two hands. You shouldn't be picking one and hard-coding it here.


    • Don't have multiple hard-coded sort methods. Have one method that takes a comparator. Otherwise every time any client wants a new sort method, the API needs to change.


    • Maybe revealed would be better than flipped? I also think that the revealed state of a card belongs here, not on the Card.


    Playing Card



    • Does isFaceUp make sense on PlayingCard? I guess it might if your deck can have face-up cards in it. But strongly consider if that should be external state. That sounds like it belongs to the UI layer, not the model.


    • Return early from equals(). The reason for a single return was made obsolete years ago.


    • Strongly consider whether you want to implement compareTo(). Some games have trump suits which can change the natural ordering of the cards in the deck.


    I took a stab at a generic deck class, and came up with this. It should probably have a draw(int cards) and overloaded discard methods also, but I need to pack.



    //Note that this class is *NOT* thread safe. It will definitely have problems in a multi-threaded environment.
    public final class Deck<T extends Card>

    private final List<T> deck = new LinkedList<>();
    private final Set<T> outstandingCards = new HashSet<>();
    private final List<T> discardPile = new LinkedList<>();

    public Deck(final Collection<T> cards)
    super();
    this.deck.addAll(cards);


    public void shuffle()
    this.deck.addAll(this.discardPile);
    this.discardPile.clear();
    Collections.shuffle(this.deck);


    public boolean isDeckEmpty()
    return this.deck.isEmpty();


    public int deckSize()
    return this.deck.size();


    public T draw()
    if (this.isDeckEmpty())
    throw new IllegalStateException();


    final T card = this.deck.remove(0);
    this.outstandingCards.add(card);
    return card;


    public void discard(final T card)
    if (!this.outstandingCards.contains(card))
    // Somebody tried to sneak a new card into the deck!
    throw new IllegalStateException();

    this.outstandingCards.remove(card);
    this.discardPile.add(card);







    share|improve this answer





















    • Good points! One minor thing I'd like to note is that shuffling multiple times can actually have an effect I think. The basic Collections#shuffle seems to be using Java's default Random, which if I am not mistaken uses a 48-bit seed, way less than 52!. So shuffling multiple times would allow you to perhaps reach more states? That does not mean it is required of course, and if it is, it probably needs to be implemented differently.
      – Koekje
      May 11 at 11:02











    • Thank you for your review. I'm still processing your comments. Mind blown. You're exactly right, I do have UI code mixed with business rules. I need to separate. You're also spot on with the YAGNI comment. Many methods were added to anticipate a need. What's meant by a card state? CardState.FACE_UP? I get the concept of a shoe. However, why is clear() and (re)populateDeck() not a good strategy? Worth the overhead to discard and add again? Hand#calculateValue is for Blackjack--which is my intended purpose. Great idea! I'll refactor to sort(Comparator c)
      – Frijolie
      May 12 at 16:00












    up vote
    2
    down vote










    up vote
    2
    down vote









    Every competent non-junior developer is self-taught, because you don't learn everything you need to know in school.



    This is actually a reasonably tricky problem you've picked out, so don't be discouraged by all the feedback. You need to spend more time thinking about the bounds between application-specific code (Poker rules, Uno rules, etc) and the generic library code (How does a Deck work, what does a Card look like). Right now it's all mixed together. That's not surprising - building good APIs is hard.



    It also seems like you have a lot of methods that serve no particular purpose because you think they might be handy later. It's really easy to add and really hard to remove, and YAGNI. Wait for a use case before adding things, and make sure you do it right, because if you have to fix it all your clients will break.



    Card



    • Use javadoc, not comments


    • equals and hashCode aren't doing anything. All objects have them, so every Card will always fulfill that part of the contract without doing any extra work.


    • Consider having flip() return the new state of the card.


    • What is the point of this interface? If you're only building for poker cards, the interface is noise. If you're building for other kinds of cards, you're forcing a ton of false requirements - Uno cards, for instance, can't fulfill this contract. Strongly consider making this a marker interface if you want to support non-poker games.


    • The methods are mixed-up. On the one hand, clients can ask your for your internals and you freely share them. On the other hand, the card renders itself with no inputs. Either keep your privates private and use instead methods like hasRank(Rank rank) or get rid of the render method and let the client do it.


    Color



    • Having a class with the same name as a class in the Java API is a bad idea. You're asking for conflicts. It's not really a Color, it's a CardColor or some such.

    Deck



    • Deck needs to be final. You can't have users extending Deck with their own functionality (like putting all the good cards in one place when "shuffling")s


    • A deck is a well-understood concept. It's nonsensical to create a deck and specify the number of decks it has. You need a separate concept, a Shoe. Shoes can hold multiple decks of poker cards in them.


    • You don't need to clear a new Deck. It's already clear.


    • This isn't a generic Deck class. It's a poker deck. Do you want something useful for other games? If so, you need to take all the poker functionality into a separate class.


    • Shuffling more than once in a row is not sensical in code. The deck will be no more shuffled after the eighth time than the first.


    • isShuffled() is not useful. After you call shuffle, the deck is shuffled. Unless you add some way to unshuffle a deck?


    • I doubt the utility of removeCard(Card) and hasCard(Card). I guess there might be games where they are helpful, but I can't think of any.


    • It's visually ugly to use the return value of Objects.requireNonNull() like that. Declare it as a standalone call.


    • populateDeck() again assumes a poker deck. And it's public, which means any yahoo with access to the deck can throw in cards at any time.


    • clearDeck() is the same problem. And what does it mean for a deck to be cleared? In real life, you don't ever get rid of all the cards, still have a "deck", and then add cards back in. You might take cards out of the deck or put them back in, but that's different than the abstraction you have.


    • What's the use case for giving out a list of the cards? You should return a List, not a Collection, because decks typically have an order.


    • A deck is usually not a stack. A Deque might be more appropriate.


    • pop is leaking internal details. Nobody says "pop me a card off that deck".


    Hand



    • Needs to be final for the same reason as Deck.


    • Is there a use case for removing a specific card from a Hand?


    • Again, keep the display code out of the model. Either pass in something to do the rendering or have the UI call methods on Hand to render.


    • calculateValue - is there a use case for this? What game uses as a value the sum of all the cards?


    • clearHand - this needs a better name, because that's not what people talk about doing in real life. Also, it should return the cards you just tossed, to facilitate putting them back in the deck. Or maybe pass in a deck and add the cards back to it?


    • compareTo - there are lots of different ways to compare the strengths of two hands. You shouldn't be picking one and hard-coding it here.


    • Don't have multiple hard-coded sort methods. Have one method that takes a comparator. Otherwise every time any client wants a new sort method, the API needs to change.


    • Maybe revealed would be better than flipped? I also think that the revealed state of a card belongs here, not on the Card.


    Playing Card



    • Does isFaceUp make sense on PlayingCard? I guess it might if your deck can have face-up cards in it. But strongly consider if that should be external state. That sounds like it belongs to the UI layer, not the model.


    • Return early from equals(). The reason for a single return was made obsolete years ago.


    • Strongly consider whether you want to implement compareTo(). Some games have trump suits which can change the natural ordering of the cards in the deck.


    I took a stab at a generic deck class, and came up with this. It should probably have a draw(int cards) and overloaded discard methods also, but I need to pack.



    //Note that this class is *NOT* thread safe. It will definitely have problems in a multi-threaded environment.
    public final class Deck<T extends Card>

    private final List<T> deck = new LinkedList<>();
    private final Set<T> outstandingCards = new HashSet<>();
    private final List<T> discardPile = new LinkedList<>();

    public Deck(final Collection<T> cards)
    super();
    this.deck.addAll(cards);


    public void shuffle()
    this.deck.addAll(this.discardPile);
    this.discardPile.clear();
    Collections.shuffle(this.deck);


    public boolean isDeckEmpty()
    return this.deck.isEmpty();


    public int deckSize()
    return this.deck.size();


    public T draw()
    if (this.isDeckEmpty())
    throw new IllegalStateException();


    final T card = this.deck.remove(0);
    this.outstandingCards.add(card);
    return card;


    public void discard(final T card)
    if (!this.outstandingCards.contains(card))
    // Somebody tried to sneak a new card into the deck!
    throw new IllegalStateException();

    this.outstandingCards.remove(card);
    this.discardPile.add(card);







    share|improve this answer













    Every competent non-junior developer is self-taught, because you don't learn everything you need to know in school.



    This is actually a reasonably tricky problem you've picked out, so don't be discouraged by all the feedback. You need to spend more time thinking about the bounds between application-specific code (Poker rules, Uno rules, etc) and the generic library code (How does a Deck work, what does a Card look like). Right now it's all mixed together. That's not surprising - building good APIs is hard.



    It also seems like you have a lot of methods that serve no particular purpose because you think they might be handy later. It's really easy to add and really hard to remove, and YAGNI. Wait for a use case before adding things, and make sure you do it right, because if you have to fix it all your clients will break.



    Card



    • Use javadoc, not comments


    • equals and hashCode aren't doing anything. All objects have them, so every Card will always fulfill that part of the contract without doing any extra work.


    • Consider having flip() return the new state of the card.


    • What is the point of this interface? If you're only building for poker cards, the interface is noise. If you're building for other kinds of cards, you're forcing a ton of false requirements - Uno cards, for instance, can't fulfill this contract. Strongly consider making this a marker interface if you want to support non-poker games.


    • The methods are mixed-up. On the one hand, clients can ask your for your internals and you freely share them. On the other hand, the card renders itself with no inputs. Either keep your privates private and use instead methods like hasRank(Rank rank) or get rid of the render method and let the client do it.


    Color



    • Having a class with the same name as a class in the Java API is a bad idea. You're asking for conflicts. It's not really a Color, it's a CardColor or some such.

    Deck



    • Deck needs to be final. You can't have users extending Deck with their own functionality (like putting all the good cards in one place when "shuffling")s


    • A deck is a well-understood concept. It's nonsensical to create a deck and specify the number of decks it has. You need a separate concept, a Shoe. Shoes can hold multiple decks of poker cards in them.


    • You don't need to clear a new Deck. It's already clear.


    • This isn't a generic Deck class. It's a poker deck. Do you want something useful for other games? If so, you need to take all the poker functionality into a separate class.


    • Shuffling more than once in a row is not sensical in code. The deck will be no more shuffled after the eighth time than the first.


    • isShuffled() is not useful. After you call shuffle, the deck is shuffled. Unless you add some way to unshuffle a deck?


    • I doubt the utility of removeCard(Card) and hasCard(Card). I guess there might be games where they are helpful, but I can't think of any.


    • It's visually ugly to use the return value of Objects.requireNonNull() like that. Declare it as a standalone call.


    • populateDeck() again assumes a poker deck. And it's public, which means any yahoo with access to the deck can throw in cards at any time.


    • clearDeck() is the same problem. And what does it mean for a deck to be cleared? In real life, you don't ever get rid of all the cards, still have a "deck", and then add cards back in. You might take cards out of the deck or put them back in, but that's different than the abstraction you have.


    • What's the use case for giving out a list of the cards? You should return a List, not a Collection, because decks typically have an order.


    • A deck is usually not a stack. A Deque might be more appropriate.


    • pop is leaking internal details. Nobody says "pop me a card off that deck".


    Hand



    • Needs to be final for the same reason as Deck.


    • Is there a use case for removing a specific card from a Hand?


    • Again, keep the display code out of the model. Either pass in something to do the rendering or have the UI call methods on Hand to render.


    • calculateValue - is there a use case for this? What game uses as a value the sum of all the cards?


    • clearHand - this needs a better name, because that's not what people talk about doing in real life. Also, it should return the cards you just tossed, to facilitate putting them back in the deck. Or maybe pass in a deck and add the cards back to it?


    • compareTo - there are lots of different ways to compare the strengths of two hands. You shouldn't be picking one and hard-coding it here.


    • Don't have multiple hard-coded sort methods. Have one method that takes a comparator. Otherwise every time any client wants a new sort method, the API needs to change.


    • Maybe revealed would be better than flipped? I also think that the revealed state of a card belongs here, not on the Card.


    Playing Card



    • Does isFaceUp make sense on PlayingCard? I guess it might if your deck can have face-up cards in it. But strongly consider if that should be external state. That sounds like it belongs to the UI layer, not the model.


    • Return early from equals(). The reason for a single return was made obsolete years ago.


    • Strongly consider whether you want to implement compareTo(). Some games have trump suits which can change the natural ordering of the cards in the deck.


    I took a stab at a generic deck class, and came up with this. It should probably have a draw(int cards) and overloaded discard methods also, but I need to pack.



    //Note that this class is *NOT* thread safe. It will definitely have problems in a multi-threaded environment.
    public final class Deck<T extends Card>

    private final List<T> deck = new LinkedList<>();
    private final Set<T> outstandingCards = new HashSet<>();
    private final List<T> discardPile = new LinkedList<>();

    public Deck(final Collection<T> cards)
    super();
    this.deck.addAll(cards);


    public void shuffle()
    this.deck.addAll(this.discardPile);
    this.discardPile.clear();
    Collections.shuffle(this.deck);


    public boolean isDeckEmpty()
    return this.deck.isEmpty();


    public int deckSize()
    return this.deck.size();


    public T draw()
    if (this.isDeckEmpty())
    throw new IllegalStateException();


    final T card = this.deck.remove(0);
    this.outstandingCards.add(card);
    return card;


    public void discard(final T card)
    if (!this.outstandingCards.contains(card))
    // Somebody tried to sneak a new card into the deck!
    throw new IllegalStateException();

    this.outstandingCards.remove(card);
    this.discardPile.add(card);








    share|improve this answer













    share|improve this answer



    share|improve this answer











    answered May 11 at 1:15









    Eric Stein

    3,703512




    3,703512











    • Good points! One minor thing I'd like to note is that shuffling multiple times can actually have an effect I think. The basic Collections#shuffle seems to be using Java's default Random, which if I am not mistaken uses a 48-bit seed, way less than 52!. So shuffling multiple times would allow you to perhaps reach more states? That does not mean it is required of course, and if it is, it probably needs to be implemented differently.
      – Koekje
      May 11 at 11:02











    • Thank you for your review. I'm still processing your comments. Mind blown. You're exactly right, I do have UI code mixed with business rules. I need to separate. You're also spot on with the YAGNI comment. Many methods were added to anticipate a need. What's meant by a card state? CardState.FACE_UP? I get the concept of a shoe. However, why is clear() and (re)populateDeck() not a good strategy? Worth the overhead to discard and add again? Hand#calculateValue is for Blackjack--which is my intended purpose. Great idea! I'll refactor to sort(Comparator c)
      – Frijolie
      May 12 at 16:00
















    • Good points! One minor thing I'd like to note is that shuffling multiple times can actually have an effect I think. The basic Collections#shuffle seems to be using Java's default Random, which if I am not mistaken uses a 48-bit seed, way less than 52!. So shuffling multiple times would allow you to perhaps reach more states? That does not mean it is required of course, and if it is, it probably needs to be implemented differently.
      – Koekje
      May 11 at 11:02











    • Thank you for your review. I'm still processing your comments. Mind blown. You're exactly right, I do have UI code mixed with business rules. I need to separate. You're also spot on with the YAGNI comment. Many methods were added to anticipate a need. What's meant by a card state? CardState.FACE_UP? I get the concept of a shoe. However, why is clear() and (re)populateDeck() not a good strategy? Worth the overhead to discard and add again? Hand#calculateValue is for Blackjack--which is my intended purpose. Great idea! I'll refactor to sort(Comparator c)
      – Frijolie
      May 12 at 16:00















    Good points! One minor thing I'd like to note is that shuffling multiple times can actually have an effect I think. The basic Collections#shuffle seems to be using Java's default Random, which if I am not mistaken uses a 48-bit seed, way less than 52!. So shuffling multiple times would allow you to perhaps reach more states? That does not mean it is required of course, and if it is, it probably needs to be implemented differently.
    – Koekje
    May 11 at 11:02





    Good points! One minor thing I'd like to note is that shuffling multiple times can actually have an effect I think. The basic Collections#shuffle seems to be using Java's default Random, which if I am not mistaken uses a 48-bit seed, way less than 52!. So shuffling multiple times would allow you to perhaps reach more states? That does not mean it is required of course, and if it is, it probably needs to be implemented differently.
    – Koekje
    May 11 at 11:02













    Thank you for your review. I'm still processing your comments. Mind blown. You're exactly right, I do have UI code mixed with business rules. I need to separate. You're also spot on with the YAGNI comment. Many methods were added to anticipate a need. What's meant by a card state? CardState.FACE_UP? I get the concept of a shoe. However, why is clear() and (re)populateDeck() not a good strategy? Worth the overhead to discard and add again? Hand#calculateValue is for Blackjack--which is my intended purpose. Great idea! I'll refactor to sort(Comparator c)
    – Frijolie
    May 12 at 16:00




    Thank you for your review. I'm still processing your comments. Mind blown. You're exactly right, I do have UI code mixed with business rules. I need to separate. You're also spot on with the YAGNI comment. Many methods were added to anticipate a need. What's meant by a card state? CardState.FACE_UP? I get the concept of a shoe. However, why is clear() and (re)populateDeck() not a good strategy? Worth the overhead to discard and add again? Hand#calculateValue is for Blackjack--which is my intended purpose. Great idea! I'll refactor to sort(Comparator c)
    – Frijolie
    May 12 at 16:00










    up vote
    1
    down vote













    In Deck.java you have:



    if(deck.contains(Objects.requireNonNull(card))) {


    but you have a function containsCard that could shorten it:



    if(containsCard(card))





    share|improve this answer



























      up vote
      1
      down vote













      In Deck.java you have:



      if(deck.contains(Objects.requireNonNull(card))) {


      but you have a function containsCard that could shorten it:



      if(containsCard(card))





      share|improve this answer

























        up vote
        1
        down vote










        up vote
        1
        down vote









        In Deck.java you have:



        if(deck.contains(Objects.requireNonNull(card))) {


        but you have a function containsCard that could shorten it:



        if(containsCard(card))





        share|improve this answer















        In Deck.java you have:



        if(deck.contains(Objects.requireNonNull(card))) {


        but you have a function containsCard that could shorten it:



        if(containsCard(card))






        share|improve this answer















        share|improve this answer



        share|improve this answer








        edited May 10 at 19:31


























        answered May 10 at 18:58









        depperm

        30319




        30319






















             

            draft saved


            draft discarded


























             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f194134%2fmy-first-deck-of-cards%23new-answer', 'question_page');

            );

            Post as a guest













































































            Popular posts from this blog

            Chat program with C++ and SFML

            Function to Return a JSON Like Objects Using VBA Collections and Arrays

            Will my employers contract hold up in court?