4.5. Instance Variables and PropertiesIn Chapter 3, we declared all of an application's variables in the Main method. Variables declared in the body of a particular method are local variables and can be used only in that method. When a method terminates, the values of its local variables are lost. In contrast, an object's attributes are carried with the object as it is used in a program. Such attributes exist before a method is called on the object, while the method is executing, and after the method completes execution. Attributes are represented as variables in a class declaration. Such variables are called instance variables and are declared inside a class declaration but outside the bodies of the class's other members, such as methods (and properties, which are discussed later in this section). Each object of a class maintains its own copy of an instance variablethat is, each object (instance) of the class has a separate instance of the variable in memory. [Note: In Chapter 9, Classes and Objects: A Deeper Look, we discuss another type of variable called a Shared variable, where all objects of a class share one copy of the variable.] The following example demonstrates a GradeBook class that contains a courseNameValue instance variable to represent a particular GradeBook object's course name. GradeBook Class with an Instance Variable and a PropertyThe next version of class GradeBook (Fig. 4.7) maintains the course name as instance variable courseNameValue (line 5) so that the course name can be used or modified at any time during an application's execution. The class also contains one methodDisplayMessage (lines 1924)and one propertyCourseName (line 816). Recall from Chapter 2 that properties are used to manipulate an object's attributes. For example, in that chapter, we used a Label's Text property to specify the text to display on the Label. In this example, we use a property in code rather than in the Properties window of the IDE. To do this, we first declare a property as a member of the GradeBook class. As you'll soon see, the GradeBook's CourseName property can be used to store a course name in a GradeBook (in instance variable courseNameValue) or retrieve the GradeBook's course name (from instance variable courseNameValue). Method DisplayMessagewhich now specifies no parametersstill displays a welcome message that includes the course name. However, the method now uses the CourseName property to obtain the course name from instance variable courseNameValue. Figure 4.7. GradeBook class that contains a courseNameValue instance variable and a CourseName property.
A typical instructor teaches more than one course, each with its own course name. Line 5 declares instance variable courseNameValue as a String. Line 5 is a declaration for an instance variable because it is in the body of the class (lines 425) but outside the bodies of the class's method (lines 1924) and property (lines 816). Every GradeBook object requires its own copy of instance variable courseNameValue because each object represents a GradeBook for a different course. All the methods and properties of class GradeBook can directly manipulate its instance variable courseNameValue, but it is considered good practice for methods to use properties to manipulate instance variables (as we do in line 23 of method DisplayMessage)we will see why in Section 4.8, Validating Data with Set Accessors in Properties. Note that line 23 contains the identifier, vbCrLf, which is not declared explicitly in the program. Identifier vbCrLf is one of several predefined constants provided by Visual Basic. Constants contain values that programmers cannot modify. The constant vbCrLf represents a combination of the carriage return and linefeed characters. Outputting this constant's value causes subsequent text to display at the beginning of the next line. The effect of this constant is similar to calling Console.WriteLine(). Although not demonstrated in this example, Visual Basic also provides the vbTab constant, which represents a Tab character. Both the vbCrLf and vbTab constants are defined in the Constants module. To view the complete list of constants, select Help > Index and type Constants module in the Look for field. Then select Constants module from the index list to view all of Visual Basic's predefined constants. Access Modifiers Public and PrivateMost instance variable declarations are preceded with the access modifier Private (as in line 5 of Fig. 4.7). Variables, methods and properties that are declared Private are accessible only to methods and properties of the class in which they are declared. Note that the keyword Dim is replaced by Private in an instance variable declaration. Declaring instance variables with access modifier Private is known as information hiding. When a program creates (instantiates) an object of class GradeBook, variable courseNameValue is encapsulated (hidden) in the object and can be accessed only by methods and properties of the object's class. Software Engineering Observation 4.1
Software Engineering Observation 4.2
Setting and Getting the Values of Private Instance VariablesHow can we allow a program to manipulate a class's Private instance variables but ensure that they remain in a valid state? We need to provide controlled ways for programmers to "get" (i.e., retrieve) the value in an instance variable and "set" (i.e., modify) the value in an instance variable. For these purposes, programmers using languages other than Visual Basic normally use methods known as get and set methods. These methods typically are made Public, and provide ways for the client to access or modify Private data. Historically, these methods begin with the words "get" and "set"in our class GradeBook for example, if we were to use such methods they might be called GetCourseNameValue and SetCourseNameValue, respectively. Although it is possible to define methods with these names, Visual Basic properties provide a more elegant solution. GradeBook Class with a PropertyThe GradeBook class's CourseName property declaration is located in lines 816 of Fig. 4.7. The property begins in line 8 with an access modifier (in this case, Public), followed by the keyword Property, the property's nameCourseNameand an empty set of parentheses. The keywords As String indicate that property CourseName represents a String (which in this example is the instance variable courseNameValue). Properties contain accessors that handle the details of returning and modifying data. A property declaration can contain a Get accessor, a Set accessor or both. The Get accessor (lines 911) enables a client to read the value of Private instance variable courseNameValue; the Set accessor (lines 1315) enables a client to modify courseNameValue. After defining a property, you can use it like a variable in your code. For example, you can assign a value to a property using an assignment statement. This executes the code in the property's Set accessor to set the value of the corresponding instance variable. Similarly, referencing the property to use its value (for example, to display it on the screen) executes the code in the property's Get accessor to obtain the corresponding instance variable's value. We will demonstrate how to use properties in a program shortly. When we use properties in our examples, our convention is to append "Value" to instance variable names (e.g., courseNameValue). Visual Basic is not case sensitive, so we cannot have both an instance variable named courseName and a property named CourseName in the same class. Get and Set AccessorsLet us look more closely at property CourseName's Get and Set accessors (Fig. 4.7). The Get accessor (lines 911) begins with the keyword Get and ends with the keywords End Get. The accessor's body contains a Return statement, which consists of the keyword Return followed by an expression. The expression's value is returned to the client code that references the property. In this example, the value of courseNameValue is returned when the property CourseName is referenced. For example, the following statement Dim theCourseName As String = gradeBook.CourseName executes property CourseName's Get accessor, which returns the value of instance variable courseNameValue. That value is then stored in variable theCourseName. Note that property CourseName can be used as simply as if it were an instance variable. The property notation allows the client to think of the property as the underlying data. Again, the client cannot directly manipulate instance variable courseNameValue because it is Private. The Set accessor (lines 1315) begins with the keyword Set and ends with the keywords End Set. Following the keyword Set in line 13 is a pair of parentheses enclosing the Set accessor's parameter. When the property CourseName appears in an assignment statement, as in gradeBook.CourseName = "CS100 Introduction to Computers" the text "CS100 Introduction to Computers" is automatically passed to the parameter named value in line 13, and the Set accessor executes. Line 14 then stores value in instance variable courseNameValue. Set accessors do not return any data when they complete their tasks. When you type the first line of a property declaration in the IDE and press Enter, the IDE creates empty Get and Set accessors for you. The parameter for the Set accessor is named value by default, so we use this parameter name in our examples. You can choose a different name if you like. The statements inside the property's accessors at lines 10 and 14 (Fig. 4.7) each access courseNameValue even though it was declared (as Private) outside the accessors. We can use variable courseNameValue in the methods and properties of class GradeBook because courseNameValue is an instance variable of the class. The order in which methods and properties are declared in a class does not determine when they are called at execution time, so you can declare method DisplayMessage (which uses property CourseName) before you declare property CourseName. Within the property itself, the Get and Set accessors can appear in any order, and either accessor can be omitted. In Chapter 9, we discuss how to omit either a Set or Get accessor to create a "read-only" or "write-only" property, respectively. Using Property CourseName in Method DisplayMessageMethod DisplayMessage (lines 1924 of Fig. 4.7) does not receive any parameters. Lines 2223 output a welcome message that includes the value of instance variable courseNameValue. We do not reference courseNameValue directly. Instead, we access property CourseName (line 23), which automatically executes the property's Get accessor, returning the value of courseNameValue. GradeBookTest Module That Demonstrates Class GradeBookModule GradeBookTest (Fig. 4.8) creates a GradeBook object and demonstrates property CourseName. Line 8 creates a GradeBook object that is referenced by variable gradeBook of type GradeBook. Lines 1112 display the initial course name by referencing the object's CourseName propertythis executes the property's Get accessor, which returns the value of courseNameValue. Note that the first line of the output does not contain a course name. Variables have a default initial valuea value provided by Visual Basic when the programmer does not specify the variable's initial value. The default value for numeric types like Integer is zero. As we will see in the next section, String is a reference type; the default value for reference types is Nothinga keyword which indicates that a variable does not yet refer to an object. When you display a String variable that contains the value Nothing, no text is displayed on the screen. Figure 4.8. Creating and manipulating a GradeBook object (invoking properties).
Line 15 prompts the user to enter a course name. Local String variable theName (declared in line 18) is initialized with the course name entered by the user, which is returned by the call to Console.ReadLine(). Line 20 assigns theName to the gradeBook object's CourseName property. When a value is assigned to CourseName, the value specified (in this case, theName) is assigned to parameter value (line 13 of Fig. 4.7) of CourseName's Set accessor (lines 1315, Fig. 4.7). Then parameter value is assigned by the Set accessor to instance variable courseNameValue (line 14 of Fig. 4.7). Line 21 (Fig. 4.8) displays a blank line, then line 24 calls gradeBook's DisplayMessage method to display the welcome message containing the course name. GradeBook's UML Class Diagram with a PropertyFigure 4.9 contains an updated UML class diagram for the version of class GradeBook in Fig. 4.7. We model properties in the UML as attributesthe property (in this case, CourseName) is listed as a public attributeas indicated by the plus (+) signpreceded by the word "Property" in guillemets (« and »). Using descriptive words in guillemets (called stereotypes in the UML) helps distinguish properties from other attributes and operations. The UML indicates the type of the property by placing a colon and a type after the property name. The Get and Set accessors of the property are implied (otherwise, the property would be inaccessible), so they are not listed in the UML diagram. In some cases, a property may require only a Get accessor to allow client code to retrieve the property's value. This is known as a "read-only" property. You can indicate this in a class diagram by placing the annotation {ReadOnly} after the property's type. Figure 4.9. UML class diagram indicating that class GradeBook has a courseNameValue attribute of type String, one property and one method.
The diagram shows that class GradeBook also contains one Public method DisplayMessage, which the class diagram lists this operation in the third compartment. Recall that the plus (+) sign before an operation name indicates that the operation is Public. When we demonstrated how to declare a property in Visual Basic code, you saw that we typically name a property the same as the instance variable it manipulates, but with a capital first letter (e.g., property CourseName manipulates instance variable courseName). A class diagram helps you design a class, so it is not required to show every implementation detail of the class. Since an instance variable that is manipulated by a property is really an implementation detail of that property, our class diagram does not show the courseNameValue instance variable. A programmer implementing the GradeBook class based on this class diagram would create the instance variable courseNameValue as part of the implementation process (as we did in Fig. 4.7). In some cases, you may find it necessary to model the Private instance variables of a class, because they are not implementation details of properties. Like properties, instance variables are attributes of a class and are modeled in the middle compartment of a class diagram. The UML represents instance variables as attributes by listing the attribute name, followed by a colon and the attribute type. To indicate that an attribute is private, a class diagram would list a minus sign () before the attribute's name. For example, the instance variable courseNameValue in Fig. 4.7 would be modeled as "courseNameValue : string" Software Engineering with Properties and Set and Get AccessorsUsing properties as described earlier in this section would seem to violate the notion of Private data. Although providing a property with Get and Set accessors may appear to be the same as making its corresponding instance variable Public, this is not the case. A Public instance variable can be read or written by any method in the program. If an instance variable is Private, the client code can access the instance variable only indirectly through the class's non-Private methods or properties. This allows the class to control the manner in which the data is set or returned. For example, Get and Set accessors can translate between the format of the data used by the client and the format stored in the Private instance variable. Consider a Clock class that represents the time of day as a Private Integer instance variable timeValue containing the number of seconds since midnight. Suppose the class provides a Time property of type String to manipulate this instance variable. Although Get accessors typically return data exactly as it is stored in an object, they need not expose the data in this "raw" format. When a client refers to a Clock object's Time property, the property's Get accessor could use instance variable timeValue to determine the number of hours, minutes and seconds since midnight, then return the time as a String of the form "HH:MM:SS". Similarly, suppose a Clock object's Time property is assigned a String of the form "HH:MM:SS". Using the String capabilities presented in Section 4.8 and the method Convert.ToInt32 presented in Section 7.10, the Time property's Set accessor could convert this String to an Integer number of seconds and store the result in the Clock object's Private instance variable timeValue. The Time property's Set accessor can also provide data validation capabilities that scrutinize attempts to modify the instance variable's value to ensure that the value it receives represents a valid time (e.g., "12:30:45" is valid but "42:85:70" is not). We demonstrate data validation in Section 4.8. So, although a property's accessors enable clients to manipulate Private data, they carefully control those manipulations and the object's Private data remains safely encapsulated (i.e., hidden) in the object. This is not possible with Public instance variables, which can easily be set by clients to invalid values. Properties of a class should also be used by the class's own methods to manipulate the class's Private instance variables, even though the methods can directly access the Private instance variables. Accessing an instance variable via a property's accessorsas in the body of method DisplayMessage (Fig. 4.7, line 23)creates a better, more robust class that is easier to maintain and less likely to malfunction. If we decide to change the representation of instance variable courseNameValue in some way, the declaration of method DisplayMessage will not require modificationonly the bodies of property CourseName's Get and Set accessors that directly manipulate the instance variable will need to change. For example, suppose that we want to represent the course name as two separate instance variablescourseNumber (e.g., "CS101") and courseTitle (e.g., "Introduction to Visual Basic Programming"). The DisplayMessage method can still use property CourseName's Get accessor to obtain the full course name to display as part of the welcome message. In this case, the Get accessor would need to build and return a String containing the courseNumber followed by the courseTitle. Method DisplayMessage would continue to display the complete course title "CS101 Introduction to Visual Basic Programming," because it is unaffected by the change to the class's instance variables. Software Engineering Observation 4.3
|