Section 10.4. Relationship between Base Classes and Derived Classes


10.4. Relationship between Base Classes and Derived Classes

In this section, we use a business-oriented inheritance hierarchy containing types of employees in a company's payroll application to discuss the relationship between a base class and a derived class. Commission employees (who will be represented as objects of a base class) are paid a percentage of their sales, while base-salaried commission employees (who will be represented as objects of a derived class) receive a base salary plus a percentage of their sales. We divide our discussion of the relationship between commission employees and base-salaried commission employees into a carefully paced series of five examples:

  1. In the first example, we create class CommissionEmployee, which contains as Private instance variables a first name, last name, social security number, commission rate (percentage) and gross (i.e., total) sales amount.

  2. The second example defines class BasePlusCommissionEmployee, which contains as Private instance variables a first name, last name, social security number, commission rate, gross sales amount and a base salary. We create the class by writing every line of code that the class requireswe will soon see that it is much more efficient to create this class simply by inheriting from class CommissionEmployee, then adding appropriate attributes and behaviors.

  3. The third example defines a new version of class BasePlusCommissionEmployee that inherits directly from class CommissionEmployee (i.e., a BasePlusCommissionEmployee is a CommissionEmployee who also has a base salary) and attempts to access class CommissionEmployee's Private membersthis results in compilation errors, because the derived class does not have access to the base class's Private data.

  4. The fourth example shows that if CommissionEmployee's data is declared as Protected, a new version of class BasePlusCommissionEmployee that inherits from class CommissionEmployee can access that data directly. For this purpose, we define a new version of class CommissionEmployee with Protected data. Both the inherited and noninherited BasePlusCommissionEmployee classes contain identical functionality, but we show how the version of BasePlusCommissionEmployee that inherits from class CommissionEmployee is easier to create and manage.

  5. After we discuss the convenience of using protected data, we create the fifth example, which sets the CommissionEmployee data members back to Private to enforce good software engineering. This example demonstrates that derived class BasePlusCommissionEmployee can use base class CommissionEmployee's Public properties and methods to manipulate (in a carefully controlled manner) CommissionEmployee's Private data.

10.4.1. Creating and Using a CommissionEmployee Class

We begin by declaring class CommissionEmployee (Fig. 10.4). Lines 34 begin the class declaration and indicate that class CommissionEmployee Inherits from class Object (from namespace System). Visual Basic programmers use inheritance to create classes from existing classes. In fact, every class in Visual Basic (except Object) inherits from an existing class. Because class CommissionEmployee is derived from class Object, class CommissionEmployee inherits the methods of class Objectclass Object does not have any fields. In fact, every Visual Basic class directly or indirectly inherits Object's methods. If a class does not specify that it inherits another class, the new class implicitly inherits Object. For this reason, programmers typically do not include "Inherits Object" in their codewe do so in this example for demonstration purposes, but then we omit "Inherits Object" in all subsequent examples.

Figure 10.4. CommissionEmployee class represents an employee paid a percentage of gross sales.

  1   ' Fig. 10.4: CommmissionEmployee.vb  2   ' CommissionEmployee class represents a commission employee.  3   Public Class CommissionEmployee  4      Inherits Object ' optional    5  6      Private firstNameValue As String ' first name                         7      Private lastNameValue As String ' last name                           8      Private socialSecurityNumberValue As String ' social security number  9      Private grossSalesValue As Decimal ' gross weekly sales              10      Private commissionRateValue As Decimal ' commission percentage       11 12      ' five-argument constructor 13      Public Sub New(ByVal first As String, ByVal last As String, _ 14         ByVal ssn As String, ByVal sales As Decimal, ByVal rate As Decimal) 15 16         ' implicit call to Object constructor occurs here          17         FirstName = first                                          18         LastName = last                                            19         SocialSecurityNumber = ssn                                 20         GrossSales = sales ' validate and store gross sales        21         CommissionRate = rate ' validate and store commission rate 22      End Sub ' New 23 24      ' property FirstName 25      Public Property FirstName() As String 26         Get 27            Return firstNameValue 28         End Get 29 30         Set(ByVal first As String) 31            firstNameValue = first ' no validation 32         End Set 33      End Property ' FirstName 34 35      ' property LastName 36      Public Property LastName() As String 37         Get 38            Return lastNameValue 39         End Get 40 41         Set(ByVal last As String) 42            lastNameValue = last ' no validation 43         End Set 44      End Property ' LastName 45 46      ' property SocialSecurityNumber 47      Public Property SocialSecurityNumber() As String 48         Get 49            Return socialSecurityNumberValue 50         End Get 51 52         Set(ByVal ssn As String) 53            socialSecurityNumberValue = ssn ' no validation 54         End Set 55      End Property ' SocialSecurityNumber 56 57      ' property GrossSales 58      Public Property GrossSales() As Decimal 59         Get 60            Return grossSalesValue 61         End Get 62 63         Set(ByVal sales As Decimal) 64            If sales < 0.0 Then ' validate gross sales 65               grossSalesValue = 0                     66            Else                                       67               grossSalesValue = sales                 68            End If                                     69         End Set 70      End Property ' GrossSales 71 72      ' property CommissionRate 73      Public Property CommissionRate() As Decimal 74         Get 75            Return commissionRateValue 76         End Get 77 78         Set(ByVal rate As Decimal) 79            If rate > 0.0 AndAlso rate < 1.0 Then ' validate rate 80               commissionRateValue = rate                         81            Else                                                  82               commissionRateValue = 0                            83            End If                                                84         End Set 85      End Property ' CommissionRate 86 87      ' calculate earnings                            88      Public Function CalculateEarnings() As Decimal  89         Return commissionRateValue * grossSalesValue 90      End Function ' CalculateEarnings                91 92      ' return String representation of CommissionEmployee object    93      Public Overrides Function ToString() As String                 94         Return ("commission employee: " & firstNameValue & " " & _  95            lastNameValue & vbCrLf & "social security number: " & _  96            socialSecurityNumberValue & vbCrLf & "gross sales: " & _ 97            String.Format("{0:C}", grossSalesValue) & vbCrLf & _     98            "commission rate: " & String.Format("{0:F}", _           99            commissionRateValue))                                    100     End Function ' ToString                                        101  End Class ' CommissionEmployee 

The Public services of class CommissionEmployee include a constructor (lines 1322), properties FirstName (lines 2533), LastName (lines 3644), SocialSecurityNumber (lines 4755), GrossSales (lines 5870) and CommissionRate (lines 7385), and methods CalculateEarnings (lines 8890) and ToString (lines 93100). Lines 2585 declare Public properties for manipulating the class's instance variables firstNameValue, lastNameValue, socialSecurityNumberValue, grossSalesValue and commissionRateValue (declared in lines 610). Class CommissionEmployee declares each of its instance variables as Private, so objects of other classes cannot directly access these variables. Properties GrossSales and CommissionRate validate their arguments before assigning the values to instance variables grossSalesValue and commissionRateValue, respectively.

Constructors are not inherited, so class CommissionEmployee does not inherit class Object's constructor. However, class CommissionEmployee's constructor calls class Object's constructor implicitly. In fact, the first task of any derived class constructor is to call its direct base class's constructor, either explicitly or implicitly (if no constructor call is specified), to ensure that the instance variables inherited from the base class are initialized properly. The syntax for calling a base class constructor explicitly is discussed in Section 10.4.3. If the code does not include an explicit call to the base class constructor, Visual Basic implicitly calls the base class's default or parameterless constructor. The comment in line 16 of Fig. 10.4 indicates where the implicit call to the base class Object's default constructor is made (you do not need to write the code for this call). Object's default constructor does nothing. Note that even if a class does not have constructors, the default constructor that the compiler implicitly declares for the class will call the base class's default or parameterless constructor.

After the implicit call to Object's constructor occurs, lines 1721 of CommissionEmployee's constructor assign values to the class's instance variables. Note that we do not validate the values of arguments first, last and ssn before assigning them to the corresponding instance variables. We could validate the first and last namesperhaps by ensuring that they are of a reasonable length. Similarly, a social security number could be validated to ensure that it contains nine digits, with or without dashes (e.g., 123-45-6789 or 123456789).

Method CalculateEarnings (lines 8890) calculates a CommissionEmployee's earnings. Line 89 multiplies the commissionRateValue by the grossSalesValue and returns the result.

Method ToString (lines 93100) of class CommissionEmployee Overrides (redefines) class Object's ToString method. When invoked, CommissionEmployee's ToString method returns a String containing information about the CommissionEmployee. Without overriding ToString in class CommissionEmployee, the default implementation would return "CommissionEmployeeTest.CommissionEmployee", where CommissionEmployeeTest is the default namespace (which is the same as the project name).

A base class method must be declared Overridable if it is to be overridden in a derived class. Method ToString of class Object is declared Overridable, so derived class CommissionEmployee can override this method. To view the method header for ToString, select Help > Index..., and enter "Object.ToString method" in the search text box. The page displayed contains a description of method ToString, which includes the following header:

 Public Overridable Function ToString() As String 


Common Programming Error 10.1

It is a compilation error to attempt to override a method that is not declared Overridable.


Common Programming Error 10.2

It is a compilation error to override a method with a method that has a different access modifier than the method being overridden.


Testing Class CommissionEmployee

Figure 10.5 tests class CommissionEmployee. Lines 67 instantiate a CommissionEmployee object and invoke CommissionEmployee's constructor (lines 1322 of Fig. 10.4) to initialize it with "Sue" as the first name, "Jones" as the last name, "222-22-2222" as the social security number, 10000 as the gross sales amount and 0.06 as the commission rate. Lines 1016 access CommissionEmployee's Public properties for output. Lines 1819 change the values of properties GrossSales and CommissionRate. Lines 2224 call CommissionEmployee's ToString method to output the string representation of the updated CommissionEmployee. Lines 2728 display the CommissionEmployee's earnings, calculated by the object's CalculateEarnings method using the updated values of instance variables grossSalesValue and commissionRateValue.

Figure 10.5. CommissionEmployee class test program.

  1  ' Fig. 10.5: CommissionEmployeeTest.vb  2  ' Testing class CommissionEmployee.  3  Module CommissionEmployeeTest  4     Sub Main()  5        ' instantiate CommissionEmployee object            6        Dim employee As New CommissionEmployee( _          7           "Sue", "Jones", "222-22-2222", 10000, 0.06D)  8  9        ' get commission employee data 10        Console.WriteLine("Employee information obtained by properties:" _ 11           & vbCrLf & "First name is " & employee.FirstName & vbCrLf & _ 12           "Last name is " & employee.LastName & vbCrLf & _ 13           "Social Security Number is " & employee.SocialSecurityNumber) 14        Console.WriteLine("Gross sales is {0:C}", employee.GrossSales) 15        Console.WriteLine("Commission rate is {0:F}", _ 16           employee.CommissionRate) 17 18        employee.GrossSales = 500 ' set gross sales                 19        employee.CommissionRate = 0.1D ' set commission rate to 10% 20 21        ' get new employee information 22        Console.WriteLine(vbCrLf & _ 23           "Updated employee information obtained by ToString: " & _ 24           vbCrLf & employee.ToString() & vbCrLf) 25 26        ' display the employee's earnings 27        Console.WriteLine("Employee's earnings: {0:C}", _ 28           employee.CalculateEarnings()) 29     End Sub ' Main 30  End Module ' CommissionEmployeeTest 

 Employee information obtained by properties: First name is Sue Last name is Jones Social Security Number is 222-22-2222 Gross sales is $10,000.00 Commission rate is 0.06 Updated employee information obtained by ToString: commission employee: Sue Jones social security number: 222-22-2222 gross sales: $500.00 commission rate: 0.10 Employee's earnings: $50.00 



10.4.2. Creating a BasePlusCommissionEmployee Class without Using Inheritance

We now discuss the second part of our introduction to inheritance by declaring and testing a completely new and independent class BasePlusCommissionEmployee (Fig. 10.6), which contains a first name, last name, social security number, gross sales amount, commission rate and base salary.

Figure 10.6. BasePlusCommissionEmployee class represents an employee who receives a base salary in addition to a commission.

  1   ' Fig. 10.6: BasePlusCommissionEmployee.vb  2   ' BasePlusCommissionEmployee class represents an employee that receives  3   ' a base salary in addition to a commission.  4   Public Class BasePlusCommissionEmployee  5      Private firstNameValue As String ' first name  6      Private lastNameValue As String ' last name  7      Private socialSecurityNumberValue As String ' social security number  8      Private grossSalesValue As Decimal ' gross weekly sales  9      Private commissionRateValue As Decimal ' commission percentage 10      Private baseSalaryValue As Decimal ' base salary per week 11 12      ' six-argument constructor 13      Public Sub New(ByVal first As String, ByVal last As String, _ 14         ByVal ssn As String, ByVal sales As Decimal, _ 15         ByVal rate As Decimal, ByVal salary As Decimal) 16 17         ' implicit call to Object constructor occurs here 18         FirstName = first 19         LastName = last 20         SocialSecurityNumber = ssn 21         GrossSales = sales ' validate and store gross sales 22         CommissionRate = rate ' validate and store commission rate 23         BaseSalary = salary ' validate and store base salary 24      End Sub ' New 25 26      ' property FirstName 27      Public Property FirstName() As String 28         Get 29            Return firstNameValue 30         End Get 31 32         Set(ByVal first As String) 33            firstNameValue = first ' no validation 34         End Set 35      End Property ' FirstName 36 37      ' property LastName 38      Public Property LastName() As String 39         Get 40            Return lastNameValue 41         End Get 42 43         Set(ByVal last As String) 44            lastNameValue = last ' no validation 45         End Set 46      End Property ' LastName 47 48      ' property SocialSecurityNumber 49      Public Property SocialSecurityNumber() As String 50         Get 51            Return socialSecurityNumberValue 52         End Get 53 54         Set(ByVal ssn As String) 55            socialSecurityNumberValue = ssn ' no validation 56         End Set 57      End Property ' SocialSecurityNumber 58 59      ' property GrossSales 60      Public Property GrossSales() As Decimal 61         Get 62            Return grossSalesValue 63         End Get 64 65         Set(ByVal sales As Decimal) 66            If sales < 0.0 Then ' validate gross sales 67               grossSalesValue = 0 68            Else 69               grossSalesValue = sales 70            End If 71         End Set 72      End Property ' GrossSales 73 74      ' property CommissionRate 75      Public Property CommissionRate() As Decimal 76         Get 77            Return commissionRateValue 78         End Get 79 80         Set(ByVal rate As Decimal) 81            If rate > 0.0 AndAlso rate < 1.0 Then ' validate rate 82               commissionRateValue = rate 83            Else 84               commissionRateValue = 0 85            End If 86         End Set 87      End Property ' CommissionRate 88 89      ' property BaseSalary                             90      Public Property BaseSalary() As Decimal           91         Get                                            92            Return baseSalaryValue                      93         End Get                                        94       95         Set(ByVal salary As Decimal)                   96            If salary < 0.0 Then ' validate base salary 97               baseSalaryValue = 0                      98            Else                                        99               baseSalaryValue = salary                 100           End If                                      101        End Set                                        102     End Property ' BaseSalary                         103 104     ' calculate earnings 105     Public Function CalculateEarnings() As Decimal 106        Return baseSalaryValue + (commissionRateValue * grossSalesValue) 107     End Function ' CalculateEarnings 108 109     ' return String representation of BasePlusCommissionEmployee object 110     Public Overrides Function ToString() As String 111        Return ("base-plus-commission employee: " & firstNameValue & " " & _ 112           lastNameValue & vbCrLf & "social security number: " & _ 113           socialSecurityNumberValue & vbCrLf & "gross sales: " & _ 114           String.Format("{0:C}", grossSalesValue) & vbCrLf & _ 115           "commission rate: " & String.Format("{0:F}", _ 116           commissionRateValue) & vbCrLf & "base salary: " & _ 117           String.Format("{0:C}", baseSalaryValue)) 118     End Function ' ToString 119  End Class ' BasePlusCommissionEmployee 

Defining Class BasePlusCommissionEmployee

Class BasePlusCommissionEmployee's Public services include a BasePlusCommissionEmployee constructor (lines 1324), properties FirstName (lines 2735), LastName (lines 3846), SocialSecurityNumber (lines 4957), GrossSales (lines 6072), CommissionRate (lines 7587) and BaseSalary (lines 90102), and methods CalculateEarnings (lines 105107) and ToString (lines 110118). Lines 27102 declare Public properties for the class's Private instance variables firstNameValue, lastNameValue, socialSecurityNumberValue, grossSalesValue, commissionRateValue and baseSalaryValue (declared in lines 510). These variables, properties and methods comprise all the necessary features of a base-salaried commission employee. Note the similarity between this class and class CommissionEmployee (Fig. 10.4)in this example, we do not yet exploit that similarity.

Note that class BasePlusCommissionEmployee does not specify "Inherits Object," so the class implicitly inherits Object. Also, like class CommissionEmployee's constructor (lines 1322 of Fig. 10.4), class BasePlusCommissionEmployee's constructor invokes class Object's default constructor implicitly, as noted in the comment in line 17 (Fig. 10.6).

Class BasePlusCommissionEmployee's CalculateEarnings method (lines 105107) computes the earnings of a base-salaried commission employee. Line 106 returns the result of adding the base salary to the product of the commission rate and the gross sales. Class BasePlusCommissionEmployee overrides Object method ToString (lines 110118) to return a String containing the BasePlusCommissionEmployee's information.

Testing Class BasePlusCommissionEmployee

Figure 10.7 tests class BasePlusCommissionEmployee. Lines 67 instantiate a BasePlusCommissionEmployee object and pass "Bob", "Lewis", "333-33-3333", 5000, 0.04 and 300 to the constructor as the first name, last name, social security number, gross sales, commission rate and base salary, respectively. Lines 1017 use BasePlusCommissionEmployee's properties to retrieve the values of the object's instance variables for output. Line 19 changes the BaseSalary property. Property BaseSalary's Set accessor (Fig. 10.6, lines 95101) ensures that instance variable baseSalaryValue is not assigned a negative value, because an employee's base salary cannot be negative. Line 24 of Fig. 10.7 invokes the object's ToString method explicitly to get the object's string representation.

Figure 10.7. BasePlusCommissionEmployee test program.

  1  ' Fig. 10.7: BasePlusCommissionEmployeeTest.vb  2  ' Testing class BasePlusCommissionEmployee.  3  Module BasePlusCommissionEmployeeTest  4     Sub Main()  5        ' instantite BasePlusCommissionEmployee object       6        Dim employee As New BasePlusCommissionEmployee( _    7           "Bob", "Lewis", "333-33-3333", 5000, 0.04D, 300)  8  9        ' get base-salaried commission employee data 10        Console.WriteLine("Employee information obtained by properties:" _ 11           & vbCrLf & "First name is " & employee.FirstName & vbCrLf & _ 12           "Last name is " & employee.LastName & vbCrLf & _ 13           "Social Security Number is " & employee.SocialSecurityNumber) 14        Console.WriteLine("Gross sales is {0:C}", employee.GrossSales) 15        Console.WriteLine("Commission rate is {0:F}", _ 16           employee.CommissionRate) 17        Console.WriteLine("Base salary is {0:C}", employee.BaseSalary) 18 19        employee.BaseSalary = 1000 ' set base salary 20 21        ' get new employee information 22        Console.WriteLine(vbCrLf & _ 23           "Updated employee information obtained by ToString: " & _ 24           vbCrLf & employee.ToString() & vbCrLf) 25 26        ' display the employee's earnings 27        Console.WriteLine("Employee's earnings: $" & _ 28           employee.CalculateEarnings()) 29     End Sub ' Main 30  End Module ' BasePlusCommissionEmployeeTest 

 Employee information obtained by properties: First name is Bob Last name is Lewis Social Security Number is 333-33-3333 Gross sales is $5,000.00 Commission rate is 0.04 Base salary is $300.00 Updated employee information obtained by ToString: base-plus-commission employee: Bob Lewis social security number: 333-33-3333 gross sales: $5,000.00 commission rate: 0.04 base salary: $1,000.00 Employee's earnings: $1200.00 



Exploring the Similarities between Class BasePlusCommissionEmployee and Class CommissionEmployee

Much of the code for class BasePlusCommissionEmployee (Fig. 10.6) is similar, if not identical, to the code for class CommissionEmployee (Fig. 10.4). For example, both classes contain Private instance variables firstNameValue, lastNameValue, socialSecurityNumberValue, grossSalesValue and commissionRateValue, and properties FirstName, LastName, SocialSecurityNumber, GrossSales and CommissionRate to manipulate these variables. The BasePlusCommissionEmployee constructor is almost identical to that of class CommissionEmployee, except that BasePlusCommissionEmployee's constructor also sets the BaseSalary property. The other additions to class BasePlusCommissionEmployee are Private instance variable baseSalaryValue and property BaseSalary. Class BasePlusCommissionEmployee's ToString method is nearly identical to that of class CommissionEmployee except that BasePlusCommissionEmployee's ToString also outputs the value of instance variable baseSalaryValue.

To form class BasePlusCommissionEmployee, we copied the code from class CommissionEmployee and pasted it into class BasePlusCommissionEmployee, then modified class BasePlusCommissionEmployee to include a base salary and methods that manipulate the base salary. This "copy-and-paste" approach is often error prone and time consuming. Worse yet, it can spread many physical copies of the same code throughout a system, creating a code-maintenance nightmare. Is there a way to "absorb" the instance variables and methods of one class in a way that makes them part of other classes without duplicating code? Indeed there isusing the elegant object-oriented programming technique of inheritance that we demonstrate in the next section.

Software Engineering Observation 10.3

Copying and pasting code from one class to another can spread errors among multiple source code files. To avoid duplicating code (and possibly errors), use inheritance, rather than the "copy-and-paste" approach, where you want one class to "absorb" the members of another class.


Software Engineering Observation 10.4

With inheritance, the common instance variables and methods of all the classes in the hierarchy are declared in a base class. When changes are required for these common features, software developers need to make the changes only in the base classderived classes then inherit the changes. Without inheritance, the changes would need to be made to all the source code files that contain copies of the code in question.


10.4.3. Creating a CommissionEmployeeBasePlusCommissionEmployee Inheritance Hierarchy

Now we declare class BasePlusCommissionEmployee (Fig. 10.9), which inherits from class CommissionEmployee (Fig. 10.8). Class CommissionEmployee is almost identical to Fig. 10.4, except that method CalculateEarnings (Fig. 10.8, lines 8890) is now declared Overridable so that a derived class of CommissionEmployee can override method CalculateEarnings to provide an appropriate earnings calculation. A BasePlusCommissionEmployee object is a CommissionEmployee (because inheritance passes on the capabilities of class CommissionEmployee), but class BasePlusCommissionEmployee also has instance variable baseSalaryValue (Fig. 10.9, line 6). Note that Fig. 10.9 does not redeclare the base class instance variables (lines 610 of Fig. 10.8)these are nevertheless present in the derived class (through inheritance). Even though they are present, they are declared Private in the base class, so as we will see in a moment, we will have to make special provision to access this base class information from the derived class. Keyword Inherits in line 4 of the class declaration (Fig. 10.9) indicates inheritance. As a derived class, BasePlusCommissionEmployee inherits the Public (and Protected, if there were any) instance variables and methods of class CommissionEmployee. The constructor of class CommissionEmployee is not inherited. Thus, the Public services of BasePlusCommissionEmployee include its constructor (lines 916), Public methods and properties inherited from class CommissionEmployee, property BaseSalary (lines 1931), method CalculateEarnings (lines 3437) and method ToString (lines 4049).

Figure 10.8. CommissionEmployee class with Overridable method CalculateEarnings.

  1   ' Fig. 10.8: CommmissionEmployee.vb  2   ' CommissionEmployee class represents a commission employee.  3   Public Class CommissionEmployee  4      Inherits Object ' optional  5  6      Private firstNameValue As String ' first name  7      Private lastNameValue As String ' last name  8      Private socialSecurityNumberValue As String ' social security number  9      Private grossSalesValue As Decimal ' gross weekly sales 10      Private commissionRateValue As Decimal ' commission percentage 11 12      ' five-argument constructor 13      Public Sub New(ByVal first As String, ByVal last As String, _ 14         ByVal ssn As String, ByVal sales As Decimal, ByVal rate As Decimal) 15 16         ' implicit call to Object constructor occurs here 17         FirstName = first 18         LastName = last 19         SocialSecurityNumber = ssn 20         GrossSales = sales ' validate and store gross sales 21         CommissionRate = rate ' validate and store commission rate 22      End Sub ' New 23 24      ' property FirstName 25      Public Property FirstName() As String 26         Get 27            Return firstNameValue 28         End Get 29 30         Set(ByVal first As String) 31            firstNameValue = first ' no validation 32         End Set 33      End Property ' FirstName 34 35      ' property LastName 36      Public Property LastName() As String 37         Get 38            Return lastNameValue 39         End Get 40 41         Set(ByVal last As String) 42            lastNameValue = last ' no validation 43         End Set 44      End Property ' LastName 45 46      ' property SocialSecurityNumber 47      Public Property SocialSecurityNumber() As String 48         Get 49            Return socialSecurityNumberValue 50         End Get 51 52         Set(ByVal ssn As String) 53            socialSecurityNumberValue = ssn ' no validation 54         End Set 55      End Property ' SocialSecurityNumber 56 57      ' property GrossSales 58      Public Property GrossSales() As Decimal 59         Get 60            Return grossSalesValue 61         End Get 62 63         Set(ByVal sales As Decimal) 64            If sales < 0.0 Then ' validate gross sales 65               grossSalesValue = 0 66            Else 67               grossSalesValue = sales 68            End If 69         End Set 70      End Property ' GrossSales 71 72      ' property CommissionRate 73      Public Property CommissionRate() As Decimal 74         Get 75            Return commissionRateValue 76         End Get 77 78         Set(ByVal rate As Decimal) 79            If rate > 0.0 AndAlso rate < 1.0 Then ' validate rate 80               commissionRateValue = rate 81            Else 82               commissionRateValue = 0 83            End If 84         End Set 85      End Property ' CommissionRate 86 87      ' calculate earnings                                       88      Public Overridable Function CalculateEarnings() As Decimal 89         Return commissionRateValue * grossSalesValue            90      End Function ' CalculateEarnings                           91 92      ' return String representation of CommissionEmployee object 93      Public Overrides Function ToString() As String 94         Return ("commission employee: " & firstNameValue & " " & _ 95            lastNameValue & vbCrLf & "social security number: " & _ 96            socialSecurityNumberValue & vbCrLf & "gross sales: " & _ 97            String.Format("{0:C}", grossSalesValue) & vbCrLf & _ 98            "commission rate: " & String.Format("{0:F}", _ 99            commissionRateValue)) 100     End Function ' ToString 101  End Class ' CommissionEmployee 

Figure 10.9. Private base class members cannot be accessed in a derived class.

  1  ' Fig. 10.9: BasePlusCommissionEmployee.vb  2  ' BasePlusCommissionEmployee inherits from class CommissionEmployee.  3  Public Class BasePlusCommissionEmployee  4     Inherits CommissionEmployee  5  6     Private baseSalaryValue As Decimal ' base salary per week  7  8     ' six-argument constructor  9     Public Sub New(ByVal first As String, ByVal last As String, _ 10        ByVal ssn As String, ByVal sales As Decimal, _ 11        ByVal rate As Decimal, ByVal salary As Decimal) 12 13        ' use MyBase to invoke CommissionEmployee constructor explicitly 14        MyBase.New(first, last, ssn, sales, rate) 15        BaseSalary = salary ' validate and store base salary 16     End Sub ' New 17 18     ' property BaseSalary 19     Public Property BaseSalary() As Decimal 20        Get 21           Return baseSalaryValue 22        End Get 23 24        Set(ByVal salary As Decimal) 25           If salary < 0.0 Then ' validate base salary 26              baseSalaryValue = 0 27           Else 28              baseSalaryValue = salary 29           End If 30        End Set 31     End Property ' BaseSalary 32 33     ' calculate earnings 34     Public Overrides Function CalculateEarnings() As Decimal 35        ' not allowed: attempts to access private base class members     36        Return baseSalaryValue + (commissionRateValue * grossSalesValue) 37     End Function ' CalculateEearnings 38 39     ' return String representation of BasePlusCommissionEmployee object 40     Public Overrides Function ToString() As String 41        ' not allowed: attempts to access private base class members         42        Return ("base-plus-commission employee: " & firstNameValue & " " & _ 43           lastNameValue & vbCrLf & "social security number: " & _           44           socialSecurityNumberValue & vbCrLf & "gross sales: " & _          45           String.Format("{0:C}", grossSalesValue) & vbCrLf & _              46           "commission rate: " & String.Format("{0:F}", _                    47           commissionRateValue) & vbCrLf & "base salary: " & _               48           String.Format("{0:C}", baseSalaryValue))                          49     End Function ' ToString 50  End Class ' BasePlusCommissionEmployee 

Each derived class constructor must implicitly or explicitly call its base class constructor to ensure that the instance variables inherited from the base class are initialized properly. BasePlusCommissionEmployee's six-argument constructor (lines 916 of Fig. 10.9) explicitly calls class CommissionEmployee's five-argument constructor to initialize the base class portion of a BasePlusCommissionEmployee object (i.e., variables firstNameValue, lastNameValue, socialSecurityNumberValue, grossSalesValue and commissionRateValue). Line 14 in BasePlusCommissionEmployee's six-argument constructor invokes the CommissionEmployee's five-argument constructor (declared at lines 1322 of Fig. 10.8) by using the base class constructor call syntaxkeyword MyBase, followed by the dot (.) separator, followed by New and a set of parentheses containing the arguments to the base class constructor. The arguments first, last, ssn, sales and rate (which were received by the derived class constructor) are used to initialize base class members firstNameValue, lastNameValue, socialSecurityNumberValue, grossSalesValue and commissionRateValue, respectively. If the BasePlusCommissionEmployee constructor did not invoke CommissionEmployee's constructor explicitly, Visual Basic would attempt to invoke class CommissionEmployee's parameterless or default constructorbut the class does not have such a constructor, so the compiler would issue an error. The explicit base class constructor call in line 14 (Fig. 10.9) must be the first statement in the derived class constructor's body. When a base class contains a default or parameterless constructor, you can use MyBase.New() to call that constructor explicitly, but this is unnecessary and is rarely done.

The compiler issues errors for line 36 of Fig. 10.9 because base class CommissionEmployee's instance variables commissionRateValue and grossSalesValue are Privatederived class BasePlusCommissionEmployee's methods are not allowed to access base class CommissionEmployee's Private members. Note that we use bold black text in Fig. 10.9 (and elsewhere in the book) to indicate erroneous code. The compiler issues additional errors at lines 4247 of BasePlusCommissionEmployee's ToString method for the same reason. The errors in BasePlusCommissionEmployee could have been prevented by using the properties inherited from class CommissionEmployee. For example, line 36 could have used properties CommissionRate and GrossSales to access CommissionEmployee's Private instance variables commissionRateValue and grossSalesValue, respectively. Lines 4247 also could have used appropriate properties to retrieve the values of the base class's instance variables.

10.4.4. CommissionEmployeeBasePlusCommissionEmployee Inheritance Hierarchy Using Protected Instance Variables

To enable class BasePlusCommissionEmployee to directly access base class instance variables firstNameValue, lastNameValue, socialSecurityNumberValue, grossSalesValue and commissionRateValue, we can declare those members as Protected in the base class. As we discussed in Section 10.3, a base class's Protected members are inherited by all derived classes of that base class and are directly accessible by derived classes.

Defining Base Class CommissionEmployee with Protected Data

Class CommissionEmployee (Fig. 10.10) modifies Fig. 10.8 to declare instance variables firstNameValue, lastNameValue, socialSecurityNumberValue, grossSalesValue and commissionRateValue as Protected (Fig. 10.10, lines 610) rather than Private. The rest of the class declaration in Fig. 10.10 is identical to the one in Fig. 10.8.

Figure 10.10. CommissionEmployee class with Protected instance variables.

  1   ' Fig. 10.10: CommmissionEmployee.vb  2   ' CommissionEmployee class represents a commission employee.  3   Public Class CommissionEmployee  4      Inherits Object ' optional  5  6      Protected firstNameValue As String ' first name                         7      Protected lastNameValue As String ' last name                           8      Protected socialSecurityNumberValue As String ' social security number  9      Protected grossSalesValue As Decimal ' gross weekly sales              10      Protected commissionRateValue As Decimal ' commission percentage       11 12      ' five-argument constructor 13      Public Sub New(ByVal first As String, ByVal last As String, _ 14         ByVal ssn As String, ByVal sales As Decimal, ByVal rate As Decimal) 15 16         ' implicit call to Object constructor occurs here 17         FirstName = first 18         LastName = last 19         SocialSecurityNumber = ssn 20         GrossSales = sales ' validate and store gross sales 21         CommissionRate = rate ' validate and store commission rate 22      End Sub ' New 23 24      ' property FirstName 25      Public Property FirstName() As String 26         Get 27            Return firstNameValue 28         End Get 29 30         Set(ByVal first As String) 31            firstNameValue = first ' no validation 32         End Set 33      End Property ' FirstName 34 35      ' property LastName 36      Public Property LastName() As String 37         Get 38            Return lastNameValue 39         End Get 40 41         Set(ByVal last As String) 42            lastNameValue = last ' no validation 43         End Set 44      End Property ' LastName 45 46      ' property SocialSecurityNumber 47      Public Property SocialSecurityNumber() As String 48         Get 49            Return socialSecurityNumberValue 50         End Get 51 52         Set(ByVal ssn As String) 53            socialSecurityNumberValue = ssn ' no validation 54         End Set 55      End Property ' SocialSecurityNumber 56 57      ' property GrossSales 58      Public Property GrossSales() As Decimal 59         Get 60            Return grossSalesValue 61         End Get 62 63         Set(ByVal sales As Decimal) 64            If sales < 0.0 Then ' validate gross sales 65               grossSalesValue = 0 66            Else 67               grossSalesValue = sales 68            End If 69         End Set 70      End Property ' GrossSales 71 72      ' property CommissionRate 73      Public Property CommissionRate() As Decimal 74         Get 75            Return commissionRateValue 76         End Get 77 78         Set(ByVal rate As Decimal) 79            If rate > 0.0 AndAlso rate < 1.0 Then ' validate rate 80               commissionRateValue = rate 81            Else 82               commissionRateValue = 0 83            End If 84         End Set 85      End Property ' CommissionRate 86 87      ' calculate earnings 88      Public Overridable Function CalculateEarnings() As Decimal 89         Return commissionRateValue * grossSalesValue 90      End Function ' CalculateEarnings 91 92      ' return String representation of CommissionEmployee object 93      Public Overrides Function ToString() As String 94         Return ("commission employee: " & firstNameValue & " " & _ 95            lastNameValue & vbCrLf & "social security number: " & _ 96            socialSecurityNumberValue & vbCrLf & "gross sales: " & _ 97            String.Format("{0:C}", grossSalesValue) & vbCrLf & _ 98            "commission rate: " & String.Format("{0:F}", _ 99            commissionRateValue)) 100     End Function ' ToString 101  End Class ' CommissionEmployee 

We could have declared the base class CommissionEmployee's instance variables firstNameValue, lastNameValue, socialSecurityNumberValue, grossSalesvalue and commissionRateValue as Public to enable derived class BasePlusCommissionEmployee to access the base class instance variables. Doing this is dangerous because it allows unrestricted access to the instance variables, greatly increasing the chance of errors. With Protected base class instance variables, the derived class gains access to the instance variables, but classes that are not derived classes of this base class cannot access these variables directly.

Modifying Derived Class BasePlusCommissionEmployee

We now modify class BasePlusCommissionEmployee (Fig. 10.9) so that it inherits from the version of class CommissionEmployee in Fig. 10.10. Because class BasePlusCommissionEmployee inherits from this new version of class CommissionEmployee, objects of class BasePlusCommissionEmployee (Fig. 10.11) inherit CommissionEmployee's Protected instance variables firstNameValue, lastNameValue, socialSecurityNumberValue, grossSalesValue and commissionRateValueall these variables are now Protected members of BasePlusCommissionEmployee. As a result, the compiler does not generate errors when compiling line 35 of method CalculateEarnings and lines 4045 of method ToString (so in Fig. 10.11 we have removed the two "not allowed" comments from Fig. 10.9 and we have changed all bold black text to black). If another class inherits BasePlusCommissionEmployee, the new derived class also inherits the Protected members.

Figure 10.11. BasePlusCommissionEmployee inherits Protected instance variables from CommissionEmployee.

  1  ' Fig. 10.11: BasePlusCommissionEmployee.vb  2  ' BasePlusCommissionEmployee inherits from class CommissionEmployee.  3  Public Class BasePlusCommissionEmployee  4     Inherits CommissionEmployee  5  6     Private baseSalaryValue As Decimal ' base salary per week  7  8     ' six-argument constructor  9     Public Sub New(ByVal first As String, ByVal last As String, _ 10        ByVal ssn As String, ByVal sales As Decimal, _ 11        ByVal rate As Decimal, ByVal salary As Decimal) 12 13        ' use MyBase reference to CommissionEmployee constructor explicitly 14        MyBase.New(first, last, ssn, sales, rate) 15        BaseSalary = salary ' validate and store base salary 16     End Sub ' New 17 18     ' property BaseSalary 19     Public Property BaseSalary() As Decimal 20        Get 21           Return baseSalaryValue 22        End Get 23 24        Set(ByVal salary As Decimal) 25           If salary < 0.0 Then ' validate base salary 26              baseSalaryValue = 0 27           Else 28              baseSalaryValue = salary 29           End If 30        End Set 31     End Property ' BaseSalary 32 33     ' calculate earnings 34     Public Overrides Function CalculateEarnings() As Decimal 35        Return baseSalaryValue + (commissionRateValue * grossSalesValue) 36     End Function ' CalculateEarnings 37 38     ' return String representation of BasePlusCommissionEmployee object 39     Public Overrides Function ToString() As String 40        Return ("base-plus-commission employee: " & firstNameValue & " " & _ 41           lastNameValue & vbCrLf & "social security number: " & _           42           socialSecurityNumberValue & vbCrLf & "gross sales: " & _          43           String.Format("{0:C}", grossSalesValue) & vbCrLf & _              44           "commission rate: " & String.Format("{0:F}", _                    45           commissionRateValue) & vbCrLf & "base salary: " & _               46           String.Format("{0:C}", baseSalaryValue))                          47     End Function ' ToString 48  End Class ' BasePlusCommissionEmployee 

Class BasePlusCommissionEmployee does not inherit class CommissionEmployee's constructor. However, class BasePlusCommissionEmployee's six-argument constructor (lines 916) calls class CommissionEmployee's five-argument constructor explicitly. BasePlusCommissionEmployee's constructor does this because CommissionEmployee does not provide a parameterless constructor that could be invoked implicitly and, more important, because arguments are being passed to the base class constructor.

Testing the Modified BasePlusCommissionEmployee Class

Fig. 10.12 uses a BasePlusCommissionEmployee object to perform the same tasks that Fig. 10.7 performed on an object of the first version of class BasePlusCommissionEmployee (Fig. 10.6). Note that the outputs of the two programs are identical. We declared the first class BasePlusCommissionEmployee without using inheritance and declared this version of BasePlusCommissionEmployee using inheritancenevertheless, both classes provide the same functionality. Note that the code for class BasePlusCommissionEmployee (Fig. 10.11), which is 48 lines, is considerably shorter than the code for the noninherited version of the class (Fig. 10.6), which is 119 lines, because the inherited version absorbs much of its functionality from base class CommissionEmployee, whereas the noninherited version absorbs only class Object's functionality.

Figure 10.12. Protected base class members inherited into derived class BasePlusCommissionEmployee.

  1  ' Fig. 10.12: BasePlusCommissionEmployeeTest.vb  2  ' Testing class BasePlusCommissionEmployee.  3  Module BasePlusCommissionEmployeeTest  4     Sub Main()  5        ' instantiate BasePlusCommissionEmployee object  6        Dim employee As New BasePlusCommissionEmployee( _  7            "Bob", "Lewis", "333-33-3333", 5000, 0.04D, 300)  8  9        ' get base-salaried commission employee data 10        Console.WriteLine("Employee information obtained by properties:" _ 11           & vbCrLf & "First name is " & employee.FirstName & vbCrLf & _ 12           "Last name is " & employee.LastName & vbCrLf & _ 13        "Social Security Number is " & employee.SocialSecurityNumber) 14        Console.WriteLine("Gross sales is {0:C}", employee.GrossSales) 15        Console.WriteLine("Commission rate is {0:F}", _ 16           employee.CommissionRate) 17        Console.WriteLine("Base salary is {0:C}", employee.BaseSalary) 18 19        employee.BaseSalary = 1000 ' set base salary 20 21        ' get new employee information 22        Console.WriteLine(vbCrLf & _ 23           "Updated employee information obtained by ToString: " & _ 24           vbCrLf & employee.ToString() & vbCrLf) 25 26        ' display the employee's earnings 27        Console.WriteLine("Employee's earnings: $" & _ 28           employee.CalculateEarnings()) 29     End Sub ' Main 30  End Module ' BasePlusCommissionEmployeeTest 

 Employee information obtained by properties: First name is Bob Last name is Lewis Social Security Number is 333-33-3333 Gross sales is $5,000.00 Commission rate is 0.04 Base salary is $300.00 Updated employee information obtained by ToString: base-plus-commission employee: Bob Lewis social security number: 333-33-3333 gross sales: $5,000.00 commission rate: 0.04 base salary: $1,000.00 Employee's earnings: $1200.00 



Notes on Using Protected Data

In this example, we declare base class instance variables as Protected so that derived classes can inherit and access them. Inheriting Protected instance variables slightly improves performance, because we can directly access the variables in the derived class without incurring the overhead of calling property Set or Get accessors. In most cases, however, it is better to use Private instance variables to encourage proper software engineering, and leave code optimization issues to the compiler. Your code will be easier to maintain, modify and debug.

Using Protected instance variables creates several potential problems. First, the derived class object can set an inherited variable's value directly without using a Set accessor. Therefore, a derived class object can assign an invalid value to the variable, thus leaving the object in an inconsistent state. For example, if we were to declare CommissionEmployee's instance variable grossSalesValue as Protected, a derived class object could then assign a negative value to grossSalesValue. Another problem with using Protected instance variables is that derived class methods are more likely to be written so that they depend on the base class's data implementation. In practice, derived classes should depend only on the base class services (i.e., non-Private methods and properties) and not on the base class data implementation. With Protected instance variables in the base class, all the derived classes of the base class may need to be modified if the base class implementation changes. For example, if for some reason we were to change the names of instance variables firstNameValue and lastNameValue to first and last, then we would have to do so for all occurrences in which a derived class directly references base class instance variables firstNameValue and lastNameValue. In such a case, the software is said to be fragile or brittle, because a small change in the base class can "break" derived class implementations. We should be able to change the base class implementation while still providing the same services to the derived classes. Of course, if the base class services change, we must reimplement our derived classes.

Software Engineering Observation 10.5

Use the Protected access modifier on a method when a base class is to provide the method to its derived classes but not to other clients.


10.4.5. CommissionEmployeeBasePlusCommissionEmployee Inheritance Hierarchy Using Private Instance Variables

We now reexamine our hierarchy once more, this time using better software engineering practices. Class CommissionEmployee (Fig. 10.13) declares instance variables firstNameValue, lastNameValue, socialSecurityNumberValue, grossSalesValue and commissionRateValue as Private (lines 48) and provides Public properties FirstName, LastName, SocialSecurityNumber, GrossSales and CommissionRate for manipulating these values. Note that methods CalculateEarnings (lines 8688) and ToString (lines 9197) use the class's properties to obtain the values of the Private instance variables. If we decide to change the instance variable names, the CalculateEarnings and ToString declarations will not require modificationonly the bodies of the Get and Set accessors that directly manipulate the instance variables will need to change. These changes would occur solely within the base classno changes to the derived class would be needed. Localizing the effects of changes is good software engineering. Derived class BasePlusCommissionEmployee (Fig. 10.14) inherits CommissionEmployee's non-Private properties and methods and can access the Private base class members via those properties and methods.

Figure 10.13. CommissionEmployee class uses properties to manipulate its Private instance variables.

  1  ' Fig. 10.13: CommmissionEmployee.vb  2  ' CommissionEmployee class represents a commission employee.  3  Public Class CommissionEmployee  4     Private firstNameValue As String ' first name                         5     Private lastNameValue As String ' last name                           6     Private socialSecurityNumberValue As String ' social security number  7     Private grossSalesValue As Decimal ' gross weekly sales               8     Private commissionRateValue As Decimal ' commission percentage        9 10     ' five-argument constructor 11     Public Sub New(ByVal first As String, ByVal last As String, _ 12        ByVal ssn As String, ByVal sales As Decimal, ByVal rate As Decimal) 13 14        ' implicit call to Object constructor occurs here 15        FirstName = first 16        LastName = last 17        SocialSecurityNumber = ssn 18        GrossSales = sales ' validate and store gross sales 19        CommissionRate = rate ' validate and store commission rate 20     End Sub ' New 21 22     ' property FirstName 23     Public Property FirstName() As String 24        Get 25           Return firstNameValue 26        End Get 27 28        Set(ByVal first As String) 29           firstNameValue = first ' no validation 30        End Set 31     End Property ' FirstName 32 33     ' property LastName 34     Public Property LastName() As String 35        Get 36           Return lastNameValue 37        End Get 38 39        Set(ByVal last As String) 40           lastNameValue = last ' no validation 41        End Set 42     End Property ' LastName 43 44     ' property SocialSecurityNumber 45     Public Property SocialSecurityNumber() As String 46        Get 47           Return socialSecurityNumberValue 48        End Get 49 50        Set(ByVal ssn As String) 51           socialSecurityNumberValue = ssn ' no validation 52        End Set 53     End Property ' SocialSecurityNumber 54 55     ' property GrossSales 56     Public Property GrossSales() As Decimal 57        Get 58           Return grossSalesValue 59        End Get 60 61        Set(ByVal sales As Decimal) 62           If sales < 0.0 Then ' validate gross sales 63              grossSalesValue = 0 64           Else 65              grossSalesValue = sales 66           End If 67        End Set 68     End Property ' GrossSales 69 70     ' property CommissionRate 71     Public Property CommissionRate() As Decimal 72        Get 73           Return commissionRateValue 74        End Get 75 76        Set(ByVal rate As Decimal) 77           If rate > 0.0 AndAlso rate < 1.0 Then ' validate rate 78              commissionRateValue = rate 79           Else 80              commissionRateValue = 0 81           End If 82        End Set 83     End Property ' CommissionRate 84 85     ' calculate earnings 86     Public Overridable Function CalculateEarnings() As Decimal 87        Return CommissionRate * GrossSales 88     End Function ' CalculateEarnings 89 90     ' return String representation of CommissionEmployee object 91     Public Overrides Function ToString() As String 92        Return ("commission employee: " & FirstName & " " & _ 93           LastName & vbCrLf & "social security number: " & _ 94           SocialSecurityNumber & vbCrLf & "gross sales: " & _ 95           String.Format("{0:C}", GrossSales) & vbCrLf & _ 96           "commission rate: " & String.Format("{0:F}", CommissionRate)) 97     End Function ' ToString 98  End Class ' CommissionEmployee 

Figure 10.14. BasePlusCommissionEmployee class Inherits CommissionEmployee, which provides only Private instance variables.

  1  ' Fig. 10.14: BasePlusCommissionEmployee.vb  2  ' BasePlusCommissionEmployee inherits from class CommissionEmployee.  3  Public Class BasePlusCommissionEmployee  4     Inherits CommissionEmployee  5  6     Private baseSalaryValue As Decimal ' base salary per week  7  8     ' six-argument constructor  9     Public Sub New(ByVal first As String, ByVal last As String, _ 10        ByVal ssn As String, ByVal sales As Decimal, _ 11        ByVal rate As Decimal, ByVal salary As Decimal) 12 13        ' use MyBase reference to CommissionEmployee constructor explicitly 14        MyBase.New(first, last, ssn, sales, rate) 15        BaseSalary = salary ' validate and store base salary 16     End Sub ' New 17 18     ' property BaseSalary 19     Public Property BaseSalary() As Decimal 20        Get 21           Return baseSalaryValue 22        End Get 23 24        Set(ByVal salary As Decimal) 25           If salary < 0.0 Then ' validate base salary 26              baseSalaryValue = 0 27           Else 28              baseSalaryValue = salary 29           End If 30        End Set 31     End Property ' BaseSalary 32 33     ' calculate earnings 34     Public Overrides Function CalculateEarnings() As Decimal 35        Return BaseSalary + MyBase.CalculateEarnings() 36     End Function ' CalculateEarnings 37 38     ' return String representation of BasePlusCommissionEmployee object 39     Public Overrides Function ToString() As String 40        Return ("base-plus-" & MyBase.ToString() & vbCrLf & _ 41           "base salary: " & String.Format("{0:C}", BaseSalary)) 42     End Function ' ToString 43  End Class ' BasePlusCommissionEmployee 

Software Engineering Observation 10.6

Declaring base class instance variables Private (as opposed to Protected) enables the base class implementation of these instance variables to change without affecting derived class implementations.


Error-Prevention Tip 10.1

When possible, do not include Protected instance variables in a base class. Instead, include non-Private properties and methods that carefully access Private instance variables. This will ensure that objects of the derived classes of this base class maintain consistent states of the base class instance variables.


Class BasePlusCommissionEmployee (Fig. 10.14) has several changes to its method implementations that distinguish it from class BasePlusCommissionEmployee (Fig. 10.11). Methods CalculateEarnings (Fig. 10.14, lines 3436) and ToString (lines 3942) both use property BaseSalary to obtain the base salary value, rather than accessing baseSalaryValue directly. If we decide to rename instance variable baseSalaryValue, only the bodies of property BaseSalary will need to change.

Class BasePlusCommissionEmployee's CalculateEarnings method (Fig. 10.14, lines 3436) overrides class CommissionEmployee's CalculateEarnings method (Fig. 10.13, lines 8688) to calculate the earnings of a base-salaried commission employee. The new version obtains the portion of the employee's earnings based on commission alone by calling CommissionEmployee's CalculateEarnings method with the expression MyBase.CalculateEarnings() (Fig. 10.14, line 35). BasePlusCommissionEmployee's CalculateEarnings method then adds the base salary to this value to calculate the total earnings of the derived-class employee. Note the syntax used to invoke an overridden base class method from a derived classplace the keyword MyBase and a dot (.) separator before the base class method name. By having BasePlusCommissionEmployee's CalculateEarnings method invoke CommissionEmployee's CalculateEarnings method to calculate part of a BasePlusCommissionEmployee object's earnings, we avoid duplicating the code and reduce code maintenance problems.

Common Programming Error 10.3

When a base class method is overridden in a derived class, the derived class version often calls the base class version to do a portion of the work. Failure to prefix the base class method name with the keyword MyBase and a dot (.) separator when referencing the base class's method causes the derived class method to call itself, creating an error called infinite recursion.


Similarly, BasePlusCommissionEmployee's ToString method (Fig. 10.14, lines 3942) overrides class CommissionEmployee's ToString method (Fig. 10.13, lines 9197) to return a string representation that is appropriate for a base-salaried commission employee. The derived class creates part of a BasePlusCommissionEmployee object's string representation (i.e., the string "base-plus-commission employee" and the values of class CommissionEmployee's Private instance variables) by appending "base-plus-" in front of the string returned by calling CommissionEmployee's ToString method with the expression MyBase.ToString() (Fig. 10.14, line 40). BasePlusCommissionEmployee's ToString method then outputs the remainder of a BasePlusCommissionEmployee object's string representation (i.e., the value of class BasePlusCommissionEmployee's base salary).

Figure 10.15 performs the same manipulations on a BasePlusCommissionEmployee object as did Fig. 10.7 and Fig. 10.12. Although each "base-salaried commission employee" class behaves identically, class BasePlusCommissionEmployee (Fig. 10.14) is the best engineered. By using inheritance and by calling methods that hide the data and ensure consistency, we have efficiently constructed a well-engineered class.

Figure 10.15. Base class Private instance variables are accessible to a derived class via the Public or Protected properties and methods inherited by the derived class.

  1  ' Fig. 10.15: BasePlusCommissionEmployeeTest.vb  2  ' Testing class BasePlusCommissionEmployee.  3  Module BasePlusCommissionEmployeeTest  4     Sub Main()  5        ' instantiate BasePlusCommissionEmployee object  6        Dim employee As New BasePlusCommissionEmployee( _  7           "Bob", "Lewis", "333-33-3333", 5000, 0.04D, 300)  8  9        ' get base-salaried commission employee data 10        Console.WriteLine("Employee information obtained by properties:" _ 11           & vbCrLf & "First name is " & employee.FirstName & vbCrLf & _ 12           "Last name is " & employee.LastName & vbCrLf & _ 13           "Social Security Number is " & employee.SocialSecurityNumber) 14        Console.WriteLine("Gross sales is {0:C}", employee.GrossSales) 15        Console.WriteLine("Commission rate is {0:F}", _ 16           employee.CommissionRate) 17        Console.WriteLine("Base salary is {0:C}", employee.BaseSalary) 18 19        employee.BaseSalary = 1000 ' set base salary 20 21        ' get new employee information 22        Console.WriteLine(vbCrLf & _ 23           "Updated employee information obtained by ToString: " & _ 24           vbCrLf & employee.ToString() & vbCrLf) 25 26        ' display the employee's earnings 27        Console.WriteLine("Employee's earnings: $" & _ 28           employee.CalculateEarnings()) 29     End Sub ' Main 30  End Module ' BasePlusCommissionEmployeeTest 

 Employee information obtained by properties: First name is Bob Last name is Lewis Social Security Number is 333-33-3333 Gross sales is $5,000.00 Commission rate is 0.04 Base salary is $300.00 Updated employee information obtained by ToString: base-plus-commission employee: Bob Lewis social security number: 333-33-3333 gross sales: $5,000.00 commission rate: 0.04 base salary: $1,000.00 Employee's earnings: $1200.00 



In this section, you studied five example programs that were carefully designed to teach key capabilities for good software engineering with inheritance. You learned how to use the keyword Inherits to create a derived class using inheritance, how to use Protected base class members to enable a derived class to access inherited base class instance variables and how to override base class methods to provide versions that are more appropriate for derived class objects. Also, you applied software engineering techniques from Chapter 9 and 10 to create classes that are easy to maintain, modify and debug.



Visual BasicR 2005 for Programmers. DeitelR Developer Series
Visual Basic 2005 for Programmers (2nd Edition)
ISBN: 013225140X
EAN: 2147483647
Year: 2004
Pages: 435

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net