I am currently learning how to write code in Java. To improve my understanding of OOP concepts, I am creating a playing card and deck class. The deck has the functionality of drawing and shuffling. I also attach an image to my card class (using JavaFX). Am I doing it right?
I am also planning to extend this class into a fully functional card game with JavaFX GUI.
Since I am self-learning, any kind of feedback would be greatly appreciated.
SuitEnum Class
public enum SuitEnum {
DIAMONDS, CLUBS, HEARTS, SPADES;
}
RankEnumClass
public enum RankEnum {
TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE;
}
Card Class
import javafx.scene.image.*;
public class Card implements Comparable<Card>{
private static final String IMAGE_FOLDER_DIR = "image";
private static final String IMAGE_FORMAT = ".png";
private static final String BACK_IMAGE_DIR = ("image/back_image.png");
private Image cardImage;
private Image backImage;
private SuitEnum suit;
private RankEnum rank;
public Card(){
}
public Card(SuitEnum suit, RankEnum rank){
this.suit = suit;
this.rank = rank;
String location = generateImageLocation();
try {
cardImage = new Image(location);
} catch (Exception ex) {
System.out.println(String.format("cannot load image from: (%s)", location));
cardImage = null;
}
try {
backImage = new Image(BACK_IMAGE_DIR);
} catch (Exception ex){
System.out.println(String.format("cannot load image from: (%s)", BACK_IMAGE_DIR));
backImage = null;
}
}
public SuitEnum getSuit() {
return suit;
}
public RankEnum getRank() {
return rank;
}
public Image getCardImage(){
return cardImage;
}
private String generateImageLocation(){
StringBuilder sb = new StringBuilder();
sb.append(IMAGE_FOLDER_DIR);
sb.append("/");
sb.append(suit.toString());
sb.append("_");
sb.append(rank.toString());
sb.append(IMAGE_FORMAT);
return sb.toString().toLowerCase();
}
@Override
public String toString(){
return (suit + " " + rank);
}
public int compareTo(Card card) {
if (this.rank.compareTo(card.rank) > 0){
return 1;
} else if (this.rank.compareTo(card.rank) < 0){
return -1;
} else {
if(this.suit.compareTo(card.suit) > 0){
return 1;
} else if (this.suit.compareTo(card.suit) < 0){
return -1;
} else {
return 0;
}
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((rank == null) ? 0 : rank.hashCode());
result = prime * result + ((suit == null) ? 0 : suit.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Card other = (Card) obj;
if (rank != other.rank)
return false;
if (suit != other.suit)
return false;
return true;
}
public Image getBackImage() {
return backImage;
}
}
}
Deck Class
import java.util.ArrayList;
import java.util.Random;
public class Deck {
private ArrayList<Card> deck = new ArrayList<>();
public Deck(){
for (SuitEnum suit: SuitEnum.values()){
for (RankEnum rank : RankEnum.values()){
deck.add(new Card(suit, rank));
}
}
}
//shuffle deck
public void shuffle(){
ArrayList<Card> tempOldDeck = new ArrayList<>();
for (Card card: deck){
tempOldDeck.add(card);
}
int[] randomPermutation = generateRandomPermutation(deck.size());
for (int i = 0; i < deck.size(); i++){
deck.set(i, tempOldDeck.get(randomPermutation[i] - 1));
}
}
private int[] generateRandomPermutation(int high){
return generateRandomPermutation(1, high);
}
private int[] generateRandomPermutation(int low, int high){
ArrayList<Integer> unselectedNumber = new ArrayList<>();
for (int i = low; i <= high; i++){
unselectedNumber.add(i);
}
Random rng = new Random();
int[] randomPermutation = new int[high - low + 1];
for (int i = 0; i < randomPermutation.length; i++){
int randomIndex = rng.nextInt(unselectedNumber.size());
randomPermutation[i] = unselectedNumber.get(randomIndex);
unselectedNumber.remove(randomIndex);
}
return randomPermutation;
}
//draw top most
public Card draw() throws EmptyDeckException{
if (deck.size() > 0){
Card drawnCard = deck.get(deck.size() - 1);
deck.remove(deck.size() - 1);
return drawnCard;
} else throw new EmptyDeckException();
}
//get deck size
public int size(){
return deck.size();
}
}
EmptyDeckException Class
public class EmptyDeckException extends Exception{
public EmptyDeckException(){
}
public EmptyDeckException(String message){
super(message);
}
public EmptyDeckException(Throwable cause){
super(cause);
}
public EmptyDeckException(String message, Throwable cause){
super(message, cause);
}
}