9.5. Time Class Case Study: Overloaded Constructors Like methods, constructors of a class can be overloaded. To do so, provide a separate method declaration with the same name (New) for each version of the method but different numbers, types and/or orders of parameters. Class Time with Overloaded Constructors The Time constructor in Fig. 9.1 initialized hourValue to 12 and minuteValue and secondValue to 0 (i.e., noon) with a call to the class's SetTime method. Recall that the declaration in line 4 of Fig. 9.2 supplied no arguments to the Time class constructor. Suppose you would like to create Time objects with any legitimate hour, minute and/or second values. Class Time of Fig. 9.3 includes five overloaded constructors to provide several ways to initialize Time objects. Each constructor calls method SetTime of the Time object, which uses the Set accessors of properties Hour, Minute and Second to ensure that the object begins in a consistent state by setting out-of-range hour, minute and second values to 12, 0 and 0, respectively. The compiler invokes the appropriate constructor by matching the number, types and order of the arguments specified in the constructor call with the number, types and order of the parameters specified in each constructor method declaration. Figure 9.3. Overloaded constructors and Optional arguments. 1 ' Fig. 9.3: Time.vb 2 ' Represents time and contains overloaded constructors. 3 Class Time 4 ' declare Integer instance variables for the hour, minute and second 5 Private hourValue As Integer ' 0 - 23 6 Private minuteValue As Integer ' 0 - 59 7 Private secondValue As Integer ' 0 - 59 8 9 ' constructor initializes hour to 12, minute to 0 and second to 0 10 ' to ensure that each Time object starts in a consistent state 11 Public Sub New () ' parameterless constructor 12 SetTime(12) ' initialize hour to noon; minute and second to 0 13 End Sub ' New 14 15 ' Time constructor: hour supplied; 16 ' minute and second default to 0 17 Public Sub New(ByVal hh As Integer) 18 SetTime(hh) ' call SetTime with one argument 19 End Sub ' New 20 21 ' Time constructor: hour and minute supplied; 22 ' second defaults to 0 23 Public Sub New (ByVal hh As Integer, ByVal mm As Integer) 24 SetTime(hh, mm) ' call SetTime with two arguments 25 End Sub ' New 26 27 ' Time constructor: hour, minute and second supplied 28 Public Sub New (ByVal hh As Integer, _ 29 ByVal mm As Integer, ByValss As Integer) 30 SetTime(hh, mm, ss) ' call SetTime with three arguments 31 End Sub ' New 32 33 ' Time constructor: another Time object supplied 34 Public Sub New(ByVal tt As Time) 35 SetTime(tt.Hour, tt.Minute, tt.Second) 36 End Sub ' New 37 38 ' set a new time value using universal time, check validity of the 39 ' data, set invalid hour to 12, set invalid minute/second to zero 40 Public Sub SetTime(Optional ByVal hh As Integer =12, _ 41 Optional ByVal mm As Integer = 0, Optional ByVal, ss As Integer = 0) 42 Hour = hh ' set hourValue using the Hour property 43 Minute = mm ' set minuteValue using the Minute property 44 Second = ss ' set secondValue using the Second property 45 End Sub ' SetTime 46 47 ' property Hour 48 Public Property Hour() As Integer 49 Get ' return hourValue 50 Return hourValue 51 End Get 52 53 Set(ByVal value As Integer) ' set hourValue 54 If (value >= 0 AndAlso value < 24) Then ' in range 0-23 55 hourValue = value ' value is valid 56 Else ' value is invalid 57 hourValue = 12 ' set to default of noon 58 End If 59 End Set 60 End Property ' Hour 61 62 ' property Minute 63 Public Property Minute() As Integer 64 Get ' return minuteValue 65 Return minuteValue 66 End Get 67 68 Set(ByVal value As Integer) ' set minuteValue 69 If (value >= 0 AndAlso value < 60) Then ' in range 0-59 70 minuteValue = value ' value is valid 71 Else ' value is invalid 72 minuteValue = 0 ' set to default of 0 73 End If 74 End Set 75 End Property ' Minute 76 77 ' property Second 78 Public Property Second() As Integer 79 Get ' return secondValue 80 Return secondValue 81 End Get 82 83 Set(ByVal value As Integer) ' set secondValue 84 If (value >= 0 AndAlso value < 60) Then ' in range 0-59 85 secondValue = value ' value is valid 86 Else ' value is invalid 87 secondValue = 0 ' set to default of 0 88 End If 89 End Set 90 End Property ' Second 91 92 ' convert Time to a String in universal-time (24-hour clock) format 93 Public Function ToUniversalString() As String 94 Returnx String.Format( "{0}:{1:D2}:{2:D2}", Hour, Minute, Second) 95 End Function ' ToUniversalString 96 97 ' convert Time to a String in standard-time (12-hour clock) format 98 Public Overrides Function ToString() As String 99 Dim suffix As String ' AM or PM suffix 100 Dim standardHour As Integer ' a standard hour in the range 1-12 101 102 ' determine whether the 12-hour clock suffix should be AM or PM 103 If Hour < 12 Then 104 suffix = " AM"' note space preceding AM 105 Else 106 suffix = " PM"' note space preceding PM 107 End If 108 109 ' convert hour from universal-time format to standard-time format 110 If (Hour = 12 OrElse Hour = 0) Then 111 standardHour = 12 112 Else 113 standardHour = Hour Mod 12 ' 1 through 11, AM or PM 114 End If 115 116 Return String.Format( "{0}:{1:D2}:{2:D2}", standardHour, Minute, _ 117 Second) & suffix 118 End Function ' ToString 119 End Class ' Time | Class Time's Constructors Because most of the code in class Time is identical to that in Fig. 9.1, this section concentrates only on the overloaded constructors. Lines 1113 define the parameterless constructor that calls SetTime (line 12), initializing the time to noon. Lines 1719 define a Time constructor with a single Integer parameter, representing the hour. Lines 2325 define a Time constructor with two Integer parameters, representing the hour and minute. Lines 2831 define a Time constructor with three Integer parameters representing the hour, minute and second. Lines 3436 define a Time constructor that receives a reference to another Time object. When this last constructorsometimes called a copy constructoris called, the values from the Time argument are copied to initialize the new object's hour-Value, minuteValue and secondValue. Class Time declares these values as Private (lines 57), but the new Time object's constructor obtains these values via the Public properties of its Time parameter by using the expressions tt.Hour, tt.Minute and tt.Second. In fact, line 35 could have accessed the argument Time object's instance variables directly with the expressions tt.hourValue, tt.minuteValue and tt.secondValue. Software Engineering Observation 9.2 | When one object of a class has a reference to another object of the same class, the first object can access all of the second object's data, methods and properties (including those that are Private). |
Although the parameter to the constructor in lines 3436 is passed by value, this does not make a copy of the Time object that is passed as the constructor's argument. Rather, it makes a copy of the reference to the Time object passed as the argument. Software Engineering Observation 9.3 | Visual Basic classes are reference types, so all Visual Basic objects are passed to methods by reference. |
Note that each constructor receives a different number and/or different types of parameters. Also, recall that if a method has one or more Optional parameters, the caller has the option of passing a value for each parameter. Method SetTime declares three Optional parametershh, mm and ss (lines 4041). Lines 12 and 18 call method SetTime with one argument, which indicates that the default values for the Optional minuteValue and secondValue parameters should be used. Line 24 calls method SetTime with two arguments, which indicates that the default value for the Optional parameter secondValue is used. Lines 30 and 35 call method SetTime with all three arguments, so that no default values are used. Common Programming Error 9.3 | A constructor can call other class methods that use instance variables not yet initialized. Using instance variables before they have been initialized can lead to logic errors. |
Using Class Time's Overloaded Constructors Figure 9.4 (TimeTest.vb) demonstrates class Time's overloaded constructors. Lines 611 create six Time objects that invoke various constructors of the class. Line 6 invokes the parameterless constructor by placing an empty set of parentheses after the class name. Lines 711 demonstrate passing arguments to the Time constructors. Line 7 invokes the constructor at lines 1719 of Fig. 9.3. Line 8 invokes the constructor at lines 2325 of Fig. 9.3. Lines 910 invoke the constructor at lines 2831 of Fig. 9.3. Line 11 invokes the constructor at lines 3436 of Fig. 9.3. Figure 9.4. Overloading Constructors. 1 ' Fig. 9.4: TimeTest.vb 2 ' Overloading constructors. 3 Module TimeTest 4 Sub Main() 5 ' use overloaded constructors 6 Dim time1 As New Time() ' constructor with zero parameters 7 Dim time2 As New Time(2) ' constructor with one parameter 8 Dim time3 As New Time(21, 34) ' constructor with two parameters 9 Dim time4 As New Time(12, 25, 42) ' three valid arguments 10 Dim time5 As New Time(27, 74, 99) ' three invalid arguments 11 Dim time6 As New Time(time4) ' copy another Time object 12 13 ' invoke time1 methods 14 Console.WriteLine("Constructed with: " & vbCrLf & _ 15 "time1: all arguments defaulted" & vbCrLf & vbTab & _ 16 time1.ToUniversalString() & vbCrLf & vbTab & _ 17 time1.ToString()) 18 ' invoke time2 methods 19 Console.WriteLine( _ 20 "time2: hour specified; minute and second defaulted" & _ 21 vbCrLf & vbTab & time2.ToUniversalString() & _ 22 vbCrLf & vbTab & time2.ToString()) 23 ' invoke time3 methods 24 Console.WriteLine( _ 25 "time3: hour and minute specified; second defaulted" & _ 26 vbCrLf & vbTab & time3.ToUniversalString() & _ 27 vbCrLf & vbTab & time3.ToString()) 28 ' invoke time4 methods 29 Console.WriteLine("time4: hour, minute and second specified" & _ 30 vbCrLf & vbTab & time4.ToUniversalString() & _ 31 vbCrLf & vbTab & time4.ToString()) 32 ' invoke time5 methods 33 Console.WriteLine( _ 34 "time5: invalid hour, minute and second specified" & _ 35 vbCrLf & vbTab & time5.ToUniversalString() & _ 36 vbCrLf & vbTab & time5.ToString()) 37 ' invoke time6 methods 38 Console.WriteLine("time6: Time object copied from time4" & _ 39 vbCrLf & vbTab & time6.ToUniversalString() & _ 40 vbCrLf & vbTab & time6.ToString()) 41 End Sub ' Main 42 End Module ' TimeTest Constructed with: time1: all arguments defaulted 12:00:00 12:00:00 PM time2: hour specified; minute and second defaulted 2:00:00 2:00:00 AM time3: hour and minute specified; second defaulted 21:34:00 9:34:00 PM time4: hour, minute and second specified 12:25:42 12:25:42 PM time5: invalid hour, minute and second specified 12:00:00 12:00:00 PM time6: Time object copied from time4 12:25:42 12:25:42 PM |
| Each Time constructor can be written to include a copy of the appropriate statements from method SetTime. This might be slightly more efficient, because it eliminates the extra SetTime call. However, consider what would happen if you change the representation of the time from three Integer values (requiring 12 bytes of memory) to a single Integer value representing the total number of elapsed seconds since midnight (requiring only 4 bytes of memory). Placing identical code in the Time constructors and method Set-Time makes such a change in the class declaration more difficult. If the implementation of SetTime changes, the implementation of all the Time constructors would need to change accordingly. If, on the other hand, the Time constructors call method SetTime directly, any changes to the implementation of SetTime must be made only once, thus reducing the likelihood of a programming error when altering the implementation. Software Engineering Observation 9.4 | If a method of a class provides functionality required by a constructor (or other method) of the class, call that method from the constructor (or other method). This simplifies the maintenance of the code and reduces the likelihood of code errors. |
|