9.2. Time Class Case StudyWe begin with a substantial application that uses most of the object-oriented programming concepts presented in Chapters 4 through 8. We introduce the Object class, the ultimate "ancestor" of all classes in Visual Basic. Also, we explain method ToString, which can be used to obtain a String representation of any Visual Basic object. Time Class DeclarationThe application consists of classes Time (Fig. 9.1) and FrmTimeTest (Fig. 9.2). Class Time contains the information needed to represent a specific time in two popular formats. Class FrmTimeTest represents a GUI for class Time. Figure 9.1. Time class declaration maintains the time in 24-hour format.
Figure 9.2. Graphical user interface for class Time.
In Fig. 9.1, lines 34 begin the Time class declaration, indicating that class Time inherits from class Object of namespace System. Recall from Chapter 7 that it is not necessary to add an assembly reference for namespace System because it is implicitly added to all projects. Visual Basic programmers use inheritance to quickly create new classes from existing classes (and, as we will see in the next chapter, to organize groups of related classes). The Inherits keyword (line 4) followed by class name Object indicates that class Time inherits the attributes and behaviors of class Object. In fact, every class (except Object) inherits either directly or indirectly from Object. If you do not include line 4, the Visual Basic compiler includes it implicitly. A complete understanding of inheritance is not necessary to understand the concepts and programs in this chapter. We explore inheritance in detail in Chapter 10. Lines 3 and 98 delimit with keywords Class and End Class, respectively, the body of the Time class declaration. Any information that we place in this body is contained within the class's scope. Class Time declares three Private Integer instance variableshour-Value, minuteValue and secondValue (lines 79)that represent the time in universaltime format (24-hour clock format). We prefer to list the instance variables of a class first, so that, when reading the code, you see the name and type of each instance variable before it is used in the class's methods. It is possible to have Private methods and Public instance variables. Private methods are called utility methods, or helper methods, because they can be called only by other methods of the class to support the operation of those methods. Using Public instance variables in a class is an uncommon and dangerous programming practice. Providing such access to a class's instance variables is unsafe; other parts of the program could accidentally or maliciously set these members to invalid values, producing potentially disastrous results. Time Class PropertiesClass Time (Fig. 9.1) declares properties Hour (lines 2739), Minute (lines 4254) and Second (lines 5769) to access instance variables hourValue, minuteValue and secondValue, respectively. Each of these properties contains a Get accessor and a Set accessor. The Set accessors (lines 3238, 4753 and 6268) strictly control the setting of the instance variables to ensure that they contain valid values. An attempt to set any instance variable to an incorrect value causes the instance variable to be set to its default value12 (noon) for the hour and 0 for the minute and secondleaving the instance variable in a consistent state. Each Get accessor (lines 2830, 4345 and 5860) returns the appropriate instance variable's value. Time Class MethodsClass Time contains the following Public methodsthe constructor New (lines 1315), SetTime (lines 1924), ToUniversalString (lines 7274) and ToString (lines 7797). Constructor New calls (in line 14) method SetTime (discussed shortly) with the hour value specified as 12, and the minute and second values specified as 0 to indicate that the default time should be noon. Visual Basic initializes Integer variables to 0 by default, so if we did not provide a constructor for class Time, instance variables hourValue, minuteValue and secondValue would each be initialized to 0, making the default time midnight. For some classes that would be fine, but we want our Time objects to be initialized to noon. Constructors are implemented as Sub procedures, not as Functions, because Sub procedures cannot return values. Generally, constructors are declared Public. Private constructors are useful but are beyond the scope of the book. As we will see, a class can have many overloaded constructorsall share the same name, New, but each must have different numbers and/or types of parameters. Method SetTime (lines 1924) uses properties Hour, Minute and Second to ensure that instance variables hourValue, minuteValue and secondValue have valid values. For example, the hourValue must be greater than or equal to 0 and less than 24, because universal-time format represents hours as integers from 0 to 23. Similarly, both the minuteValue and secondValue must fall between 0 and 59. Any values outside these ranges are invalid and default to 12 for the hourValue or 0 for the minuteValue and secondValue, ensuring that a Time object always contains valid data; that is, the object remains in a consistent state. When a user calls SetTime with invalid arguments, the program should indicate that the attempted time setting was invalid. This can be done by "throwing an exception"we discuss exception handling in Chapter 12. Method ToUniversalString (lines 7274) takes no arguments and returns a String in universal-time format, consisting of the Hour property value, two digits for the Minute property value and two digits for the Second property value. For example, if the time were 1:30:07 PM, method ToUniversalString would return the String "13:30:07". Note that format specifier D2 formats a single digit integer value with a leading 0. Method ToString (lines 7797) takes no arguments and returns a String in standard-time format, consisting of the Hour, Minute and Second property values separated by colons and followed by an AM or PM indicator (e.g., 1:27:06 PM). Lines 8293 determine the proper formatting for the hourhours from 0 to 11 print with AM, hours from 12 to 23 print with PM, hour 0 prints as 12 (midnight), hours 112 print as is and hours 1323 print as 111 (PM). Note that method ToString contains the keyword Overrides (line 77) in its declaration. Recall that every class in Visual Basic (such as class Time) inherits either directly or indirectly from class Object, which is the root of the class hierarchy. Section 10.7 summarizes class Object's seven methods, including method ToString, which returns a String representation of an object. The default implementation of ToString that every class inherits (directly or indirectly) from class Object returns the namespace and the class name of the object's class. This implementation is primarily a placeholder that can be overridden by a derived class to specify a more appropriate String representation of the data in a derived class object. For class Time, we choose to override (i.e., redefine) the ToString method to return a String which represents the Time object in standard-time (i.e., 12-hour clock) format. After defining the class, we can use it as a type in declarations such as Dim sunset As Time ' reference to object of class Time Using the Time ClassClass FrmTimeTest (Fig. 9.2) provides a GUI for testing class Time. The GUI contains three text boxes in which the user can enter values for the Time object's Hour, Minute and Second properties, respectively. Class FrmTimeTest creates an object of class Time (line 4) and assigns its reference to variable time. When the object is instantiated, New allocates the memory for the Time object, then calls the Time constructor (method New in lines 1315 of Fig. 9.1) to initialize the object's instance variables. The constructor invokes method SetTime of class Time to initialize each of the properties, setting the time to noon. Common Programming Error 9.1
Lines 2846 declare three methods that use the Time object's Hour, Minute and Second properties to alter the corresponding instance variable values. The GUI also contains a button that enables the user to increment the Second property value by 1 without having to use the corresponding text box. Method btnAddSecond_Click (lines 725) uses properties to determine and set the new time, ensuring that the values for the hour, minute and second are updated properly. For example, 23:59:59 becomes 00:00:00 when the user presses the button. Lines 5556 display the time in standard-time format (by invoking method ToString of Time) and universal-time format (by invoking method ToUniversalString of Time). Note that we are able to use class Time in class FrmTimeTest (Fig. 9.2) even though there is no Imports statement to import class Time. Classes Time and FrmTimeTest are considered to be part of the same namespace by default because we placed then in the same project directory. In fact, every class and module in Visual Basic is part of a namespace. If you do not specify a namespace for a class or module, it is placed in the default namespace, which includes the compiled classes and modules in the current directoryin Visual Studio, this is a project's directory. When a class or module uses another class in the same namespace, an Imports statement is not required. The reason we must import the classes from the .NET Framework is that our classes and modules are in different namespaces from those in the .NET Framework. For console applications, Visual Studio automatically imports namespaces System, System.Data, System.Deployment and System.Xml. For Windows applications, Visual Studio automatically imports namespaces System, System.Deployment, System.Drawing and System.Windows.Forms. Notes on the Time Class DeclarationLines 79 of class Time (Fig. 9.1) declare instance variables hourValue, minuteValue and secondValue as Private. The Time constructor initializes the instance variable hourValue to 12 and the instance variables minuteValue and secondValue to 0 (i.e., noon) to ensure that the object is created in a consistent state. The instance variables of a Time object cannot contain invalid values, because they are Private, because the constructor (which calls SetTime) is called when the Time object is created, and because method SetTime uses properties to scrutinize all subsequent attempts by a client to modify the instance variables. Methods ToUniversalString and ToString take no arguments because, by default, these methods manipulate the instance variables of the particular Time object for which they are invoked. Any method of a class can access all the instance variables of the class and can call every method of the class. This makes method calls more concise than conventional function calls in procedural programming. It also reduces the likelihood of passing the wrong arguments, the wrong types of arguments or the wrong number of arguments. Software Engineering Observation 9.1
Classes simplify programming because the clients of the class need be concerned only with its Public operations. Clients are neither aware of, nor involved in, a class's implementation. Interfaces change less frequently than implementations. When an implementation changes, implementation-dependent code must change accordingly. By hiding the implementation, we eliminate the possibility that the clients of the class will become dependent on the class's implementation details. You do not always have to create classes from scratch. Rather, you can derive classes by inheritance from other classes that provide capabilities required by the new classes. Classes also can include references to objects of other classes as membersthis is called composition. Such software reuse can greatly enhance your productivity. Section 9.7 discusses composition. Chapter 10 discusses inheritance. |