[ Exercises | Chapter Index | Main Index ]

Solution for Programming Exercise 5.1


This page contains a sample solution to one of the exercises from Introduction to Programming Using Java.


Exercise 5.1:

In all versions of the PairOfDice class in Section 5.2, the instance variables die1 and die2 are declared to be public. They really should be private, so that they would be protected from being changed from outside the class. Write another version of the PairOfDice class in which the instance variables die1 and die2 are private. Your class will need "getter" methods that can be used to find out the values of die1 and die2. (The idea is to protect their values from being changed from outside the class, but still to allow the values to be read.) Include other improvements in the class, including at least a toString() method. Test your class with a short program that counts how many times a pair of dice is rolled, before the total of the two dice is equal to two.


Discussion

The versions of the PairOfDice class in Section 2 differ in how the dice are initialized. I like the idea of initializing the dice to random values, so I will work with the following version:

public class PairOfDice {

    public int die1;   // Number showing on the first die.
    public int die2;   // Number showing on the second die.
    
    /**
     * Constructor creates a pair of dice and rolls them so that
     * they initially show some random value.
     */            
    public PairOfDice() {
        roll();  // Call the roll() method to roll the dice.
    }
    
    /**
     * Roll the dice by setting each die to be a random number between 1 and 6.
     */
    public void roll() {
        die1 = (int)(Math.random()*6) + 1;
        die2 = (int)(Math.random()*6) + 1;
    }
    
} // end class PairOfDice

After a PairOfDice object has just been created, the number on each die is definitely between 1 and 6, just like the number on a real die. Can we be sure that this will always be true? Not if the instance variables die1 and die2 are public, since they can be changed from outside the class. There is nothing to stop someone from changing them to 42 and -17 or anything else. It's not good enough to say that you're not supposed to do that with dice. I want an absolute guarantee that my dice objects can only have the values that real dice could have. By making die1 and die2 private, I can have that guarantee, because the code that I write in the PairOfDice class is the only code that will ever directly affect the values of the variables.

So, we will make die1 and die2 private, and add instance methods getDie1() and getDie2() to return the values of die1 and die2. These are "getter" methods that get the value of a member variable, and their names follow the convention that the name of a getter method consists of "get" followed by a capitalized version of the variable name. It would still be useful to be able to set the values of the dice, but we want to make it impossible to set the value to anything outside the range 1 to 6. The solution is to provide "setter" methods that throw an exception when the value is illegal.

I also added the toString() method from Subsection 5.3.3. As for other improvements, I can foresee that people who use my class will often be interested in the total on the dice, so they will tend to say things like "dice.getDie1() + dice.getDie2()" a lot. If this is going to be done over and over, why not provide a method in the class to do it? So, I added a method getTotal() that returns the total value showing on the two dice. The complete, modified PairOfDice class is shown below.

(Here is another improvement that I thought about. We could modify the roll() method so that in addition to rolling the dice, it would also return an int value giving the total on the dice. For example, this would allow us to replace "dice.roll(); val = dice.getTotal()" with "val = dice.roll()". Since it's legal to call a function with a subroutine call statement, we could still say "dice.roll()" if we just want to roll the dice without recording the total immediately. However, I decided that it was a little clearer to leave this feature out.)

The main program is easy, especially since we've done the same problem before without using objects (in Exercise 3.1). Note how the pair of dice object is used. To test whether or not the total on the dice is 2, I use the test "while (dice.getTotal() != 2)". Because of the toString() method, I just have to say say

System.out.println("The dice come up " + dice );

to show the numbers on the two dice.


The Solution



The Modified PairOfDice Class

     /**
      * An object of class PairOfDice represents a pair of dice,
      * where each die shows a number between 1 and 6.  The dice
      * can be rolled, which randomizes the numbers showing on the
      * dice.
      */
     public class PairOfDice {
     
        private int die1;   // Number showing on the first die.
        private int die2;   // Number showing on the second die.

        /**
         * Constructor creates a pair of dice and rolls them so that
         * they initially show some random value.
         */
        public PairOfDice() {
            roll();  // Call the roll() method to roll the dice.
        }
        
        /**
         * Roll the dice by setting each die to be a random number between 1 and 6.
         */
        public void roll() {
            die1 = (int)(Math.random()*6) + 1;
            die2 = (int)(Math.random()*6) + 1;
        }
                
        /**
         * Return the number showing on the first die.
         */ 
        public int getDie1() {
           return die1;
        }
        
        /**
         * Set the value of the first die.  Throws an IllegalArgumentException
         * if the value is not in the range 1 to 6.
         */
        public void setDie1( int value ) {
           if ( value < 1 || value > 6 )
              throw new IllegalArgumentException("Illegal dice value " + value);
           die1 = value;
        }
        
        /**
         * Return the number showing on the second die.
         */ 
        public int getDie2() {
           return die2;
        }
        
        /**
         * Set the value of the second die.  Throws an IllegalArgumentException
         * if the value is not in the range 1 to 6.
         */
        public void setDie2( int value ) {
           if ( value < 1 || value > 6 )
              throw new IllegalArgumentException("Illegal dice value " + value);
           die2 = value;
        }
        
        /**
         * Return the total showing on the two dice.
         */ 
        public int getTotal() {
           return die1 + die2;
        }
        
        /**
         * Return a String representation of a pair of dice, where die1
         * and die2 are instance variables containing the numbers that are
         * showing on the two dice.
         */
        public String toString() {
           if (die1 == die2)
              return "double " + die1;
           else
              return die1 + " and " + die2;
        }
        
     }  // end class PairOfDice
     
     
     
The Main Program
      
     /** 
      * Rolls a pair of dice until the dice come up snake eyes
      * (with a total value of 2).  Counts and reports the
      * number of rolls.
      */
     public class RollFor2 {
     
        public static void main(String[] args) {
           
           PairOfDice dice;  // A variable that will refer to the dice.
           int rollCount;    // Number of times the dice have been rolled.
     
           dice = new PairOfDice();  // Create the PairOfDice object.
           rollCount = 0;
           
           /* Roll the dice until they come up snake eyes. */
           
           do {
               dice.roll();
               System.out.println("The dice come up " + dice );
               rollCount++;
           } while (dice.getTotal() != 2);
           
           /* Report the number of rolls. */
           
           System.out.println("\nIt took " + rollCount + " rolls to get a 2.");
           
           /* Now, generate an exception. */
            
           System.out.println();
           System.out.println("This program will now crash with an error");
           System.out.println("when it tries to set the value of a die to 42.");
           System.out.println();
           
           dice.setDie1(42);
           System.out.println(dice);  // This statement will not be executed!
           
        }
        
     }  // end class RollFor2

[ Exercises | Chapter Index | Main Index ]