Sunday, October 16, 2011

Class definition to represent playing cards


Problem:

Implement a new class called Card that includes the following entries:
* Named constants for the four suits (CLUBS, DIAMONDS, HEARTS, SPADES) and the four ranks that are traditionally represented in words (ACE, JACK, QUEEN, KING). The values of the rank constants should be 1, 11, 12, and 13, respectively.
* A constructor that takes a rank and a suit and returns a Card with those values. • Accessor methods getRank and getSuit to retrieve the rank and suit components
of a card.
* An implementation of the toString method that returns the complete name of the card as in exercise 1. Remember that you can use the + operator to connect the different parts of the string together, as shown in the toString implementation for the Rational class in Figure 6-9.

Roberts ch 6, problem 7.

/* File: Card class
 * ----------------------------
 * Class to represent a playing card. There are named constants for suit and face card values to translate integers into respective values,
 * so a client of this class can represent this information with either the integer or the constant.
 * 
 * How you'd use this class:
 * a) Create a new playing card: "myCard = new Card (12, 2)" or "myCard = new Card (Card.QUEEN, Card.HEARTS)"
 * b) Get the rank or suit of cards: "myCardSuit = myCard.getSuit()" or "myCardRank = myCard.getRank()"
 */

public class Card {
    
    public Card (int rank, int suit){ //Constructor
        cardRank = rank; // A real program would check whether "rank" is between 1 and 13, and if not, raise an illegal arguments exception
        cardSuit = suit; // A real program would check whether "rank" is between 1 and 4, and if not, raise an illegal arguments exception
    }
 
    public String getRank() {
        switch (cardRank) {
            case ACE: return "Ace";
            case JACK: return "Jack";
            case QUEEN: return "Queen";
            case KING: return "King";
            default: return Integer.toString(cardRank); 
        }
    }
   
    public String getSuit() {
        switch (cardSuit) {
            case SPADES: return "Spades";
            case HEARTS: return "Hearts";
            case CLUBS: return "Clubs";
            case DIAMONDS: return "Diamonds";
            default: return "no suit."; // Won't actually happen but Java needs it or it thinks the switch statement is incomplete
        }
    }
    
    public String toString() {
        return ("The" + getRank() + " of " + getSuit() + ".");
    }
    
    // Instance variables keep track of the suit and rank values for the lifetime of the card object
    private int cardSuit; // Keeps track of the Card object's suit
    private int cardRank; // Keeps track of the object's rank
    
    // Named Constants to establish int values for each of the suits -- Static variables are class variables
    public static final int SPADES = 1; //does this need to be "void"...?
    public static final int HEARTS = 2;
    public static final int CLUBS = 3;
    public static final int DIAMONDS = 4;
  
    // Named constants to establish int values for each of the suits 
    public static final int ACE = 1; //How does this named constant interact w/ the rest of the program? This part is a little messed up
    public static final int JACK = 11;
    public static final int QUEEN = 12;
    public static final int KING = 13;
}
This problem came before the "Employee" class problem in the book, but I definitely think it's more confusing than writing the Employee class and only really understood it after doing problem 8. At heart, the main point of both classes is to store information about an employee instance or playing card instance and let clients of the class access the information via public methods.

This one had an extra wrench thrown in because we needed to associate integers with suit or facecard values. I completed the class definition and I get now that the benefit of having the named constants is that, even though the constructor method is expecting integers for the suit and number values, you can pass the constants instead so it's a bit more human-readable.

For example, suit information for any particular card is represented in four different places:
1) In the constructor method, the client passes the suit value through as a parameter that instantly gets stored to the instance variable.
2) The instance variable that keeps track of the suit for the lifetime of the object
3) The named constants series translates suit integers into strings. ** Question: Why do we have this level of translation instead of having the constructor look for a string instead of an integer for the "suit" argument? ** From my understanding it's because it's easier to check whether ints are between 1 and 4 than to check all the different casings and spellings people might use to input a string, so it's more bug-tolerant.
4) The getSuit() method returns the current value of the cardSuit instance variable, so a client of the class can store that information in a variable of their own.

4 comments:

  1. 3. I don't think its the string comparison, but later you may need to calculate the rank of a card, for example an ace of spades it a higher card than an ace of hearts. You'd be doing the transition from string to numeric representation to make this calculation more memory efficient.

    You can compare cases easily using: compareString.equalsIgnoreCase(String string)

    ReplyDelete
  2. Apparently, it was common practice to use this pattern (pre java-5) .. take a look at this: http://download.oracle.com/javase/1,5.0/docs/guide/language/enums.html

    I think the point of using enums of fast validation, memory overhead separating your interface from your implementation and increased type safety.

    For instance, if you used strings and mis-typed "spades" as "sapdes", this error would not be caught until run-time. However, by relying on constants (final static ints) mistyping Card.SAPDES will cause your program to not even compile. But, as that link above notes, there are problems with this approach as well.

    ReplyDelete
  3. A cleaner way is to start with our 52 Standard American card faces as vector files. You can easily modify these graphics with any vector graphics design tool like the free Inkscape or the not free and not so cheap Adobe Illustrator.

    ReplyDelete
  4. Thanks for your great information, the contents are quiet interesting.I will be waiting for your next post.
    Photo Playing Cards

    ReplyDelete