Time Class Case Study
Time1 Class Declaration
Our first example consists of two classesTime1 (Fig. 8.1) and Time1Test (Fig. 8.2). Class Time1 represents the time of day. Class Time1Test is an application class in which the main method creates one object of class Time1 and invokes its methods. These classes must be declared in separate files because they are both public classes. The output of this program appears in Fig. 8.2.
Figure 8.1. Time1 class declaration maintains the time in 24-hour format.
(This item is displayed on page 360 in the print version)
1 // Fig. 8.1: Time1.java 2 // Time1 class declaration maintains the time in 24-hour format. 3 4 public class Time1 5 { 6 private int hour; // 0 - 23 7 private int minute; // 0 - 59 8 private int second; // 0 - 59 9 10 // set a new time value using universal time; ensure that 11 // the data remains consistent by setting invalid values to zero 12 public void setTime( int h, int m, int s ) 13 14 hour = ( ( h >= 0 && h < 24 ) ? h : 0 ); // validate hour 15 minute = ( ( m >= 0 && m < 60 ) ? m : 0 ); // validate minute 16 second = ( ( s >= 0 && s < 60 ) ? s : 0 ); // validate second 17 } // end method setTime 18 19 // convert to String in universal-time format (HH:MM:SS) 20 public String toUniversalString() 21 { 22 return String.format( "%02d:%02d:%02d", hour, minute, second ); 23 } // end method toUniversalString 24 25 // convert to String in standard-time format (H:MM:SS AM or PM) 26 public String toString() 27 { 28 return String.format( "%d:%02d:%02d %s", 29 ( ( hour == 0 || hour == 12 ) ? 12 : hour % 12 ), 30 minute, second, ( hour < 12 ? "AM" : "PM" ) ); 31 } // end method toString 32 } // end class Time1 |
Figure 8.2. Time1 object used in an application.
(This item is displayed on page 362 in the print version)
1 // Fig. 8.2: Time1Test.java 2 // Time1 object used in an application. 3 4 public class Time1Test 5 { 6 public static void main( String args[] ) 7 { 8 // create and initialize a Time1 object 9 Time1 time = new Time1(); // invokes Time1 constructor 10 11 // output string representations of the time 12 System.out.print( "The initial universal time is: " ); 13 System.out.println( time.toUniversalString() ); 14 System.out.print( "The initial standard time is: " ); 15 System.out.println( time.toString() ); 16 System.out.println(); // output a blank line 17 18 // change time and output updated time 19 time.setTime( 13, 27, 6 ); 20 System.out.print( "Universal time after setTime is: " ); 21 System.out.println( time.toUniversalString() ); 22 System.out.print( "Standard time after setTime is: " ); 23 System.out.println( time.toString() ); 24 System.out.println(); // output a blank line 25 26 // set time with invalid values; output updated time 27 time.setTime( 99, 99, 99 ); 28 System.out.println( "After attempting invalid settings:" ); 29 System.out.print( "Universal time: " ); 30 System.out.println( time.toUniversalString() ); 31 System.out.print( "Standard time: " ); 32 System.out.println( time.toString() ); 33 } // end main 34 } // end class Time1Test
|
Class Time1 contains three private instance variables of type int (Fig. 8.1, lines 68)hour, minute and secondthat represent the time in universal-time format (24-hour clock format in which hours are in the range 023). Class Time1 contains public methods setTime (lines 1217), toUniversalString (lines 2023) and toString (lines 2631). These methods are also called the public services or the public interface that the class provides to its clients.
In this example, class Time1 does not declare a constructor, so the class has a default constructor that is supplied by the compiler. Each instance variable implicitly receives the default value 0 for an int. Note that instance variables also can be initialized when they are declared in the class body using the same initialization syntax as with a local variable.
Method setTime (lines 1217) is a public method that declares three int parameters and uses them to set the time. A conditional expression tests each argument to determine whether the value is in a specified range. For example, the hour value (line 14) must be greater than or equal to 0 and less than 24, because universal-time format represents hours as integers from 0 to 23 (e.g., 1 PM is hour 13 and 11 PM is hour 23; midnight is hour 0 and noon is hour 12). Similarly, both minute and second values (lines 15 and 16) must be greater than or equal to 0 and less than 60. Any values outside these ranges are set to zero to ensure that a Time1 object always contains consistent datathat is, the object's data values are always kept in range, even if the values provided as arguments to method setTime were incorrect. In this example, zero is a consistent value for hour, minute and second.
A value passed to setTime is a correct value if that value is in the allowed range for the member it is initializing. So, any number in the range 023 would be a correct value for the hour. A correct value is always a consistent value. However, a consistent value is not necessarily a correct value. If setTime sets hour to 0 because the argument received was out of range, then setTime is taking an incorrect value and making it consistent, so the object remains in a consistent state at all times. In this case, the program might want to indicate that the object is incorrect. In Chapter 13, Exception Handling, you will learn techniques that enable your classes to indicate when incorrect values are received.
Software Engineering Observation 8.1
Methods that modify the values of private variables should verify that the intended new values are proper. If they are not, the set methods should place the private variables into an appropriate consistent state. |
Method toUniversalString (lines 2023) takes no arguments and returns a String in universal-time format, consisting of six digitstwo for the hour, two for the minute and two for the second. For example, if the time were 1:30:07 PM, method toUniversalString would return 13:30:07. The return statement (line 22) uses static method format of class String to return a String containing the formatted hour, minute and second values, each with two digits and possibly a leading 0 (specified with the 0 flag). Method format is similar to method System.out.printf except that format returns a formatted String rather than displaying it in a command window. The formatted String is returned by method toUniversalString.
Method toString (lines 2631) takes no arguments and returns a String in standard-time format, consisting of the hour, minute and second values separated by colons and followed by an AM or PM indicator (e.g., 1:27:06 PM). Like method toUniversalString, method toString uses static String method format to format the minute and second as two-digit values with leading zeros if necessary. Line 29 uses a conditional operator (?:) to determine the value for hour in the stringif the hour is 0 or 12 (AM or PM), it appears as 12otherwise, the hour appears as a value from 1 to 11. The conditional operator in line 30 determines whether AM or PM will be returned as part of the String.
Recall from Section 6.4 that all objects in Java have a toString method that returns a String representation of the object. We chose to return a String containing the time in standard-time format. Method toString can be called implicitly whenever a Time1 object appears in the code where a String is needed, such as the value to output with a %s format specifier in a call to System.out.printf.
Using Class Time1
As you learned in Chapter 3, each class you declare represents a new type in Java. Therefore, after declaring class Time1, we can use it as a type in declarations such as
Time1 sunset; // sunset can hold a reference to a Time1 object
The Time1Test application class (Fig. 8.2) uses class Time1. Line 9 declares and creates a Time1 object and assigns it to local variable time. Note that new implicitly invokes class Time1's default constructor, since Time1 does not declare any constructors. Lines 1216 output the time first in universal-time format (by invoking time's toUniversalString method in line 13), then in standard-time format (by explicitly invoking time's toString method in line 15) to confirm that the Time1 object was initialized properly.
Line 19 invokes method setTime of the time object to change the time. Then lines 2024 output the time again in both formats to confirm that the time was set correctly.
To illustrate that method setTime maintains the object in a consistent state, line 27 calls method setTime with arguments of 99 for the hour, minute and second. Lines 2832 output the time again in both formats to confirm that setTime maintained the object's consistent state, then the program terminates. The last two lines of the application's output show that the time is reset to midnightthe initial value of a Time1 objectafter an attempt to set the time with three out-of-range values.
Notes on the Time1 Class Declaration
Consider several issues of class design with respect to class Time1. The instance variables hour, minute and second are each declared private. The actual data representation used within the class is of no concern to the class's clients. For example, it would be perfectly reasonable for Time1 to represent the time internally as the number of seconds since midnight or the number of minutes and seconds since midnight. Clients could use the same public methods and get the same results without being aware of this. (Exercise 8.5 asks you to represent the time in class Time1 as the number of seconds since midnight and show that there is indeed no change visible to the clients of the class.)
Software Engineering Observation 8.2
Classes simplify programming, because the client can use only the public methods exposed by the class. Such methods are usually client oriented rather than implementation oriented. Clients are neither aware of, nor involved in, a class's implementation. Clients generally care about what the class does but not how the class does it. |
Software Engineering Observation 8.3
Interfaces change less frequently than implementations. When an implementation changes, implementation-dependent code must change accordingly. Hiding the implementation reduces the possibility that other program parts will become dependent on class-implementation details. |